Cybersecurity Q&A

Browse concise answers derived from our published, source-linked cybersecurity coverage.

How are Exchange PowerShell commands formatted in the XML file used for execution?

The XML file uses a `Cmd` attribute for the command name (e.g., `Get-Mailbox`) and structures parameters with nested elements. For one parameter it uses a single property, for two parameters it uses two `Property` elements, and for four parameters it uses four. Parameters are filled programmatically using a template. Example formats are shown in the [article](/news/penetration-technique-remote-access-to-exchange-powershell), such as `Get-RoleGroupMember "Organization Management"` or `Get-Mailbox -Identity administrator`.

What are the key code differences when adapting the technique from Python2 to Python3?

The main differences involve string and bytes handling. In Python2, strings could be used directly, but Python3 requires explicit conversion of `Str` to `bytes`. For example, `.decode('utf-8')` must be replaced with `.decode('ISO-8859-1')` to avoid invisible character parsing issues. The article provides specific code adjustments for these conversions, as detailed in the [original article](/news/penetration-technique-remote-access-to-exchange-powershell).

How does the implementation achieve credential passing without a domain-joined host?

The implementation adds NTLM authentication to pass credentials, enabling remote access to Exchange PowerShell from any host. It builds upon techniques from [ProxyShell exploitation](/news/penetration-technique-python-implementation-of-exchange-powershell) and uses either pypsrp or Flask as a web proxy to filter and modify communication data, or simulates normal Exchange PowerShell communication data directly.

What is the main advantage of the remote Exchange PowerShell access technique described in the article?

The technique allows executing Exchange PowerShell commands without requiring a domain-joined host or FQDN, expanding attack surface beyond conventional methods. It leverages [NTLM authentication](/news/penetration-technique-remote-access-to-exchange-powershell) and bypasses restrictions fixed in CVE-2022–41040. This approach is particularly useful for post-ProxyShell scenarios where SSRF is patched but NTLM-enabled remote PowerShell remains accessible.

What is the significance of the `domainname` field in the `aaalogin` table for encryption analysis?

The `domainname` field distinguishes between domain users and custom users. When `domainname` equals 'ADAudit Plus Authentication', the account is a custom user. For domain users, the encryption uses a default password ('admin'), making their hashes potentially weaker. The salt field in the `aaapassword` table is irrelevant; the salt is embedded in the hash itself. This distinction is critical for targeted brute-force attacks, as highlighted in the [ADAudit Plus Exploitation Analysis — Data Encryption Analysis](/news/adaudit-plus-exploitation-analysis-data-encryption-analysis).

How can I brute-force ADAudit Plus user passwords using the encryption analysis?

Since the bcrypt hash in the `public.aaapassword` table contains the salt in the first 29 bytes (e.g., `$2a$12$...`), you can extract that salt and use it with known plaintext guesses to compute candidate hashes. If the computed hash matches the stored one, you have found the password. This technique is useful for penetration testing as described in [Penetration Basics - Obtaining Domain User Password Policies](/news/penetration-basics-obtaining-domain-user-password-policies).

Where is the encrypted password data stored in ADAudit Plus, and how can I query it?

The encrypted passwords are stored in the `public.aaapassword` table. You can query it using psql with the command: `SELECT * FROM public.aaapassword ORDER BY password_id ASC;`. To filter only custom users and their hashes, perform an inner join with `public.aaalogin` where `domainname = 'ADAudit Plus Authentication'`. Reference the [ADAudit Plus Exploitation Analysis — Data Encryption Analysis](/news/adaudit-plus-exploitation-analysis-data-encryption-analysis) for exact commands.

How does ADAudit Plus encrypt passwords for custom users and domain users?

For domain users, the system uses the default password 'admin' as plaintext and applies bcrypt with a random salt to produce the hash. For custom (non-domain) users, the actual user-provided password is used as plaintext. In both cases, the first 29 bytes of the stored hash contain the salt used for encryption. More details can be found in the [ADAudit Plus Exploitation Analysis — Data Encryption Analysis](/news/adaudit-plus-exploitation-analysis-data-encryption-analysis) article.

What should you do if you encounter garbled text or an SSL version/cipher mismatch when accessing a Fortigate SSL VPN client page?

If the response text is garbled, it may be due to x-gzip encoding; you can apply gzip decoding to obtain the original data. For the ERR_SSL_VERSION_OR_CIPHER_MISMATCH error when accessing the SSL VPN client page via a browser, the program may still return a result, and the article suggests switching to Python 2 to resolve it. These troubleshooting steps are covered in the [implementation details](/news/penetration-basics-fortigate-identification-and-version-detection) of the article.

Why does using `allow_redirects=False` in the Python requests module not work for Fortigate VPN page version detection?

The `allow_redirects=False` parameter only disables redirection when the HTTP status code is 301 or 302. In the case of Fortigate VPN login page, the redirect is returned with a status code of 200, so the parameter has no effect. Instead, you must manually parse the redirect URL from the response body using regex. This nuance is important for accurate [Fortigate identification and version detection](/news/penetration-basics-fortigate-identification-and-version-detection).

What is the key feature used for Fortigate version detection, and how is it extracted from the page source?

Each Fortigate version returns a unique 32-bit hexadecimal string in the page source code. This string can be extracted using regex matching in Python. However, note that the response may be gzip-encoded, so you need to decode it using gzip to get the original text before extraction. The full implementation is available in the [open-source code](https://github.com/3gstudent/Homework-of-Python/blob/master/Fortigate_GetVersion.py). Similar fingerprint-based version detection is used for other systems, as described in [Minio version detection](/news/penetration-basics-minio-version-detection).

How can you distinguish between a Fortigate management page and a VPN login page during penetration testing?

You can differentiate them by the redirect URL. The management page redirects to `/login?redir=%2F`, while the VPN login page redirects to `/remote/login?lang=en`. Directly accessing the IP and examining the response helps identify which page is returned. For more on similar identification techniques, see the other articles in the [Penetration Basics](/news/penetration-basics-fortigate-identification-and-version-detection) series, such as [Zimbra version detection](/news/penetration-basics-zimbra-version-detection).

How can you extract shellcode from a C program using VC6.0 DEBUG mode?

First, write a simple C program that calls an API like `MessageBoxA` and set a breakpoint. In debug mode, use Alt+8 to view the disassembly and note the call instruction's address. Then rewrite the program using inline assembly with the known API address (e.g., `0x77D507EA` for MessageBoxA on Windows XP). Debug again, open the Memory window (Alt+6) at the start of your inline assembly block, and copy the hex bytes from the beginning to the end of the call instruction. Those bytes form your shellcode. Note that this method is ASLR-dependent and only works on older systems without ASLR.

Why is it necessary to write shellcode in pure C++ without inline assembly for 64-bit environments?

Visual Studio's inline assembly (`__asm`) is not supported in 64-bit builds. To generate 64-bit shellcode, you must avoid inline assembly entirely and use pure C++ code that dynamically resolves API addresses and calls functions. This approach improves readability and debugging, and it is compatible with both x86 and x64 if you handle the differences in calling conventions (e.g., using `__fastcall` for x64). See the article for a full implementation that retrieves `GetProcAddress` and `LoadLibrary` at runtime.

What is the ShellcodeCompiler tool and how does it simplify shellcode generation?

ShellcodeCompiler is an open-source C++ tool that uses NASM to convert high-level API calls (e.g., MessageBoxA) into shellcode in bin format and assembly code. You write a Source.txt file with function declarations and calls, then run the compiler to produce the shellcode. It can also generate a test execution with the `-t` parameter. For loading the binary shellcode in your own program, you can use `VirtualAlloc` with `PAGE_EXECUTE_READWRITE` permissions, as detailed in [Windows Shellcode Study Notes - Bypassing DEP with VirtualAlloc](/news/windows-shellcode-study-notes-bypassing-dep-with-virtualalloc).

Why can't we use fixed memory addresses in shellcode on modern Windows systems like Windows 7?

Modern Windows systems implement Address Space Layout Randomization (ASLR), which randomizes the base addresses of loaded modules like kernel32.dll and user32.dll. This makes hardcoded addresses unreliable for shellcode. To overcome this, shellcode must dynamically locate API addresses at runtime, as explained in this article on [Windows Shellcode Study Notes - Generating Shellcode via Visual Studio](/news/windows-shellcode-study-notes-generating-shellcode-via-visual-studio).

What is the recommended universal method for privilege reduction from SYSTEM to a normal user, and what tool helps identify suitable parent processes?

The universal method is using SelectMyParent to spawn a process as a child of a process already running with the target user's privileges. To find such a parent, you can use `tasklist /v /fo list /fi "USERNAME eq <whoami output>"` or a custom C++ tool that differentiates between admin and standard user processes. The article includes a small tool for traversing and judging process permissions (admin or not), which is especially useful because `tasklist` alone cannot distinguish between administrator and standard user tokens. This combined approach reliably reduces privileges from SYSTEM.

How does SelectMyParent enable privilege reduction from SYSTEM to ordinary user privileges?

SelectMyParent, developed by Didier Stevens, allows creating a new process as a child of a chosen parent process. Since child processes inherit the security context of the parent, you can select a process running as an ordinary user (e.g., explorer.exe) and launch calc.exe as its child. This effectively drops SYSTEM privileges to the target user's permissions. The steps are: obtain the PID of a standard user process via `tasklist /v /fo list`, then run `SelectMyParent.exe calc.exe <PID>`. The tool also works for privilege escalation from admin to SYSTEM by choosing a SYSTEM-level parent like winlogon.exe. The article also provides a small C++ utility to determine if a process has admin privileges.

Why do common privilege reduction methods like runas and lsrunas fail when starting from SYSTEM privileges?

Methods that work from administrator to normal user (e.g., `runas`, `lsrunas`, `CPAU`, PowerShell `Start-Process -Credential`) often fail from SYSTEM because the new process is created with SYSTEM as its parent, and the token does not properly inherit the desktop or interactive session. In testing, `runas` with SYSTEM privileges reported success but the launched process (e.g., calc.exe) failed to start. `CPAU` explicitly does not support SYSTEM-level launch. The root cause is the lack of a proper logon session for the target user. A reliable workaround is using [SelectMyParent](/news/penetration-techniques-program-privilege-reduction-startup) to set the parent process to an ordinary user's process.

What are the main differences in environment variables between SYSTEM privileges and administrator privileges?

Under SYSTEM privileges, environment variables like APPDATA, Temp, Tmp, USERDOMAIN, USERNAME, and USERPROFILE point to system-level paths. For example, `echo %appdata%` returns `C:\Windows\system32\config\systemprofile\AppData\Roaming`, whereas an administrator sees `C:\Users\a\AppData\Roaming`. Similarly, the API `SHGetSpecialFolderPath` returns different paths depending on the privilege level. These differences can prevent tools like desktop capture from working correctly.