Accessing the Windows API Directly

If you are into pentesting I am sure you might have heard about the IRB shell in the Metasploit framework. This will be a small post about accessing Windows API using Railgun. Using Railgun we can access different functions in DLLs during runtime in memory. We could also write our own DLLs and call them directly using Railgun. This technique is used in the Meterpreter scripts and post exploitation modules to access the API to perform automated tasks.
For demonstration I will be using a Windows 7 machine as the target and Kali as the attacker machine.
After owning the box in the meterpreter session type “irb” and from there we can start the interactive ruby shell. The “client” will be our meterpreter client. We can access common API calls like this. Suppose I want to get the system information.

[code language=”ruby”]
client.sys.config.sysinfo
[/code]

Get the user ID

[code language=”ruby”]
client.sys.config.getuid
[/code]

View post on imgur.com


Get all network interfaces. To verify the return type of the object type .class at the end. In this case it’s an array.

[code language=”ruby”]
init = client.net.config.interfaces
init.each { |x| puts x.pretty }
[/code]

View post on imgur.com

The above are built-in calls. Using Railgun we can access the Windows API directly. The syntax would be.


Client.railgun.(DLL).(function)(arg 1, arg 2, …)

I will demonstrate some examples.
So suppose I want to access the MessageBox function in the Windows API. It’s located in the “user32” DLL.

[code language=”c”]
int WINAPI MessageBox(
_In_opt_ HWND hWnd,
_In_opt_ LPCTSTR lpText,
_In_opt_ LPCTSTR lpCaption,
_In_ UINT uType
);
[/code]
https://msdn.microsoft.com/en-us/library/windows/desktop/ms645505%28v=vs.85%29.aspx

To call the function we can type:
[code language=”ruby”]
?> client.railgun.user32.MessageBoxA(0, "Hello World", "Osanda", "MB_ICONASTERISK | MB_OK" )
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>1}
[/code]

View post on imgur.com


If you want to lock the workstation you could use “LockWorkStation” API.
BOOL WINAPI LockWorkStation(void);
https://msdn.microsoft.com/en-us/library/windows/desktop/aa376875%28v=vs.85%29.aspx

[code language=”ruby”]
?> client.railgun.user32.LockWorkStation()
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>true}
[/code]

View post on imgur.com


Suppose I want to terminate a process. For that I will be using the “OpenProcess” and “TerminateProcess” functions.

[code language=”c”]
HANDLE WINAPI OpenProcess(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwProcessId
);
[/code]

https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx

[code language=”c”]
BOOL WINAPI TerminateProcess(
_In_ HANDLE hProcess,
_In_ UINT uExitCode
);
[/code]

https://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.85%29.aspx

If I want to terminate the CMD running in the target machine. I’ll first get the handle to “PROCESS_TERMINATE” and store the return value in a variable and next call “TerminateProcess” API to terminate the process.

[code language=”ruby”]
?> client.railgun.kernel32.OpenProcess("PROCESS_TERMINATE", false, 3664)
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>4692}
>> phandle = _[‘return’]
=> 4692
>> client.railgun.kernel32.TerminateProcess(phandle, 0)
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>true}
>>
[/code]

Likewise you could do cool stuff by directly accessing the API during runtime. Read the prototype of the function and apply accordingly using Railgun.
If you want to find the functions loaded to Railgun in a specific DLL just get the exception error message and you will see the functions loaded.

View post on imgur.com


Now let’s try to add a new DLL which is not shipped by default into Railgun. To check the available DLL type.

[code language=”ruby”]
?> client.railgun.known_dll_names
=> ["kernel32", "ntdll", "user32", "ws2_32", "iphlpapi", "advapi32", "shell32", "netapi32", "crypt32", "wlanapi", "wldap32", "version"]
>>
[/code]

Let’s try to add “mpr.dll” into Railgun at runtime and try to access a function. This would be the syntax.

client.railgun.add_dll(Name, Path)

To add “mpr.dll” we can enter like this:

client.railgun.add_dll("mpr", "C:/windows/system32/mpr.dll")

After that you should add the function. To view the functions of a DLL I will be using DLL Export Viewer by Nirsoft, feel free to use any utility you like.

View post on imgur.com


I would like to use the “WNetGetUserW” function. Let’s check the function from MSDN.
[code language=”c”]
DWORD WNetGetUser(
_In_ LPCTSTR lpName,
_Out_ LPTSTR lpUserName,
_Inout_ LPDWORD lpnLength
);
[/code]
https://msdn.microsoft.com/en-us/library/windows/desktop/aa385476%28v=vs.85%29.aspx

We should follow the syntax of the Railgun.
[code language=”ruby”]
client.railgun.add_function("mpr", "WNetGetUserW", "DWORD", [
["PWCHAR", "lpName", "in"],
["PWCHAR", "lpUserName", "out"],
["PDWORD", "lpnLength", "inout"]
])
[/code]
After adding the function we can run the function passing the arguments 🙂
[code language=”ruby”]
>> client.railgun.mpr.WNetGetUserW(nil,50,50)
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>0, "username"=>"SYSTEM\x00AAAAAAAAAAAAAAAAAA", "lplen"=>50}
[/code]

That is how you can access the Windows API using Railgun. For more info about editing modules read their documentation https://github.com/rapid7/metasploit-framework/wiki/How-to-use-Railgun-for-Windows-post-exploitation
MSDN is your friend. 🙂 To play around with different APIs apply according to information provided by MSDN.

Thanks for reading.

3 thoughts on “Accessing the Windows API Directly

Leave a Reply