File Coverage

blib/lib/App/Hack/Exe.pm
Criterion Covered Total %
statement 131 142 92.2
branch 7 12 58.3
condition 1 3 33.3
subroutine 20 20 100.0
pod 2 2 100.0
total 161 179 89.9


line stmt bran cond sub pod time code
1             package App::Hack::Exe 0.000002;
2 11     11   1952995 use 5.012;
  11         44  
3 11     11   60 use warnings;
  11         16  
  11         2155  
4              
5             =head1 NAME
6              
7             App::Hack::Exe - An animated terminal app that pretends to hack a website, just
8             like in the movies
9              
10             =head1 SYNOPSIS
11              
12             require App::Hack::Exe;
13              
14             my $he = App::Hack::Exe->new;
15              
16             # Run the script
17             $he->run('yahoo.com');
18              
19             =head1 DESCRIPTION
20              
21             This is a flashy animated script that simulates a "hacking program", as often
22             seen in movies.
23              
24             =cut
25              
26             use constant {
27 11         1578 DEFAULTS => {
28             get_ipv4 => 1,
29             get_ipv6 => 1,
30             no_delay => 0,
31             ports => [qw/ 143 993 587 456 25 587 993 80 /],
32             proxies => [qw/ BEL AUS JAP CHI NOR FIN UKR /],
33             },
34             # Original ASCII art by Jan (janbrennen@github)
35             # Source: https://github.com/janbrennen/rice/blob/master/hack.exe.c
36             DEMON => <<'EOD',
37             . .
38             .n . . n.
39             . .dP dP 9b 9b. .
40             4 qXb . dX Xb . dXp t
41             dX. 9Xb .dXb __ __ dXb. dXP .Xb
42             9XXb._ _.dXXXXb dXXXXbo. .odXXXXb dXXXXb._ _.dXXP
43             9XXXXXXXXXXXXXXXXXXXVXXXXXXXXOo. .oOXXXXXXXXVXXXXXXXXXXXXXXXXXXXP
44             `9XXXXXXXXXXXXXXXXXXXXX'~ ~`OOO8b d8OOO'~ ~`XXXXXXXXXXXXXXXXXXXXXP'
45             `9XXXXXXXXXXXP' `9XX' DIE `98v8P' HUMAN `XXP' `9XXXXXXXXXXXP'
46             ~~~~~~~ 9X. .db|db. .XP ~~~~~~~
47             )b. .dbo.dP'`v'`9b.odb. .dX(
48             ,dXXXXXXXXXXXb dXXXXXXXXXXXb.
49             dXXXXXXXXXXXP' . `9XXXXXXXXXXXb
50             dXXXXXXXXXXXXb d|b dXXXXXXXXXXXXb
51             9XXb' `XXXXXb.dX|Xb.dXXXXX' `dXXP
52             `' 9XXXXXX( )XXXXXXP `'
53             XXXX X.`v'.X XXXX
54             XP^X'`b d'`X^XX
55             X. 9 ` ' P )X
56             `b ` ' d'
57             ` '
58             EOD
59             # Float; Number of seconds to display "loading" animation for
60             DOTS_DURATION => 1,
61             # Int; Number of characters to draw each "loading" animation line
62             # 77 = Width of demon
63             DOTS_WIDTH => 77,
64             # ANSI escape codes for cursor manipulation
65             MEMORIZE_CURSOR => "\e\x{37}",
66             RECALL_CURSOR => "\e\x{38}",
67 11     11   81 };
  11         48  
68              
69 11     11   72 use Carp qw/ croak /;
  11         18  
  11         1134  
70 11         2808 use Socket 1.95 qw/
71             AF_INET
72             AF_INET6
73             NI_NUMERICHOST
74             NI_NUMERICSERV
75             getaddrinfo
76             getnameinfo
77 11     11   8001 /;
  11         55399  
78 11     11   7082 use Term::ANSIColor qw/ color colored /;
  11         106680  
  11         9504  
79 11     11   2861 use Time::HiRes qw/ sleep /;
  11         6376  
  11         161  
80              
81 11         45 use fields qw/
82             get_ipv4
83             get_ipv6
84             no_delay
85             ports
86             proxies
87 11     11   6834 /;
  11         17724  
88              
89             =head1 CONSTRUCTOR
90              
91             =head2 C
92              
93             my $he = App::Hack::Exe->new( %options )
94              
95             This method constructs a new L object and returns it. Key/value
96             pair arguments may be provided to set up the initial state. The following
97             options are recognized:
98              
99             KEY DEFAULT
100             ----------- -----------
101             get_ipv4 1
102             get_ipv6 1
103             no_delay 0
104             ports [143, 993, 587, 456, 25, 587, 993, 80]
105             proxies [qw/ BEL AUS JAP CHI NOR FIN UKR /]
106              
107             =over
108              
109             =item get_ipv4, get_ipv6 (int)
110              
111             Print out this number of IPv4 and IPv6 addresses for the host name,
112             respectively.
113              
114             =item no_delay (bool)
115              
116             Print out all text at once, instead of simulating delays (e.g. network lag).
117              
118             =item ports (arrayref)
119              
120             Set the port numbers for the simulation. Ports are arbitrary strings.
121              
122             =item proxies (arrayref)
123              
124             Set the proxy names for the simulation. Proxy names are arbitrary strings.
125              
126             =back
127              
128             =cut
129              
130             sub new {
131 7     7 1 2089 my ($class, %args) = @_;
132 7         16 my App::Hack::Exe $self = $class;
133 7 50       29 unless (ref $self) {
134 7         28 $self = fields::new($class);
135             }
136 7         26375 %{$self} = (%{$self}, %{+DEFAULTS}, %args);
  7         47  
  7         17  
  7         33  
137 6         27 return $self;
138             }
139              
140             sub _colored_demon {
141 5     5   65 (my $colored = DEMON) =~ s{DIE|HUMAN}{q{color('yellow') . ${^MATCH} . color('red')}}eegp;
  10         1269  
142 5         322 return colored($colored, 'red');
143             }
144              
145             sub _dots {
146 25     25   68 my ($self, $text) = @_;
147 25         415 print $text;
148             # 10 = length of '[COMPLETE]'
149 25         81 my $num_dots = DOTS_WIDTH - 10 - length $text;
150 25         92 my $pause_for = DOTS_DURATION / $num_dots;
151 25         95 while ($num_dots --> 0) {
152 915         21609 print '.';
153 915         3139 $self->_sleep($pause_for);
154             }
155 25         236 say '[', colored('COMPLETE', 'bold green'), ']';
156 25         2157 $self->_sleep(0.6);
157 25         99 return;
158             }
159              
160             sub _get_ip {
161 5     5   18 my ($self, $hostname) = @_;
162 5         23 $self->_dots('Enumerating Target');
163 5         105 say ' [+] Host: ', $hostname;
164 5         30 my %ips = _lookup_ips($hostname);
165             my %to_get = (
166             'IPv4' => $self->{get_ipv4},
167             'IPv6' => $self->{get_ipv6},
168 5         45 );
169 5         26 foreach my $ip_type (sort keys %ips) {
170 0         0 my $addrs = $ips{$ip_type};
171 0         0 foreach my $addr (@{$addrs}) {
  0         0  
172 0 0       0 if ($to_get{$ip_type} --> 0) {
173 0         0 say " [+] $ip_type: $addr";
174             }
175             }
176             }
177 5         17 return;
178             }
179              
180             sub _lookup_ips {
181 5     5   15 my $hostname = shift;
182 5         10 my %ips;
183 5         51 my %family_map = (
184             (AF_INET) => 'IPv4',
185             (AF_INET6) => 'IPv6',
186             );
187              
188             ## no critic ( ErrorHandling::RequireCheckingReturnValueOfEval )
189             # We don't care if this succeeds, just want to keep the script from dying
190             # in the event of a network error.
191 5         25 eval {
192 5         3049 my ($err, @res) = getaddrinfo($hostname, 'echo');
193 5         33 foreach my $res (@res) {
194 0         0 my $family_key = $family_map{$res->{family}};
195             # Translate packed binary address to human-readable IP address
196             # (err, addr, port) = getnameinfo
197 0         0 my (undef, $ip) = getnameinfo($res->{addr}, NI_NUMERICHOST | NI_NUMERICSERV);
198 0 0       0 if (defined $ip) {
199 0         0 push @{$ips{$family_key}}, $ip;
  0         0  
200             }
201             }
202             };
203 5         70 return %ips;
204             }
205              
206             sub _chainproxies {
207 5     5   13 my $self = shift;
208 5         10 my @proxies = @{$self->{proxies}};
  5         60  
209 5         22 $self->_dots('Chaining proxies');
210             # Interpolation glue
211 5         17 local $" = '>';
212 5         58 my $bracket_width = length "@proxies"; # (sic)
213 5         14 my $proxy_ct = scalar @proxies;
214 5         10 my @chained;
215 5         111 print " [+] 0/$proxy_ct proxies chained {", MEMORIZE_CURSOR, (' ' x $bracket_width), '}';
216 5         24 $self->_sleep(0.2);
217 5         52 while (@proxies) {
218 30         184 push @chained, shift @proxies;
219 30         500 print "\r [+] ", (scalar @chained), RECALL_CURSOR, "@chained";
220 30         148 $self->_sleep(0.2);
221             }
222 5         126 say '';
223 5         23 return;
224             }
225              
226             sub _launchproxy {
227 5     5   13 my $self = shift;
228 5         22 $self->_dots('Opening SOCKS5 ports on infected hosts');
229 5         86 say ' [+] SSL entry point on 127.0.0.1:1337';
230 5         17 return;
231             }
232              
233             sub _portknock {
234 5     5   17 my $self = shift;
235 5         11 my @ports = @{$self->{ports}};
  5         57  
236 5         39 $self->_dots('Launching port knocking sequence');
237             # Interpolation glue
238 5         22 local $" = ',';
239 5         94 my $bracket_width = length "@ports"; # (sic)
240 5         13 my @knocked;
241 5         104 print ' [+] Knock on TCP<', MEMORIZE_CURSOR, (' ' x $bracket_width), '>', RECALL_CURSOR;
242 5         25 $self->_sleep(0.2);
243 5         30 while (@ports) {
244 28         112 push @knocked, shift @ports;
245 28         352 print $knocked[-1];
246 28 100       68 if (@ports) {
247 23         137 print $";
248             }
249 28         90 $self->_sleep(0.2);
250             }
251 5         101 say '';
252 5         28 return;
253             }
254              
255             sub _prompt {
256 5     5   29 my $self = shift;
257 5         13 my $hostname = shift;
258 5         27 $self->_sleep(0.5);
259 5         26 my $prompt = "root\@$hostname:~# ";
260 5         97 print $prompt;
261             # Wait for the user to press Ctrl-d
262 5   33     45 while (-t STDIN && ) {
263 0         0 print $prompt;
264             }
265 5         12 return;
266             }
267              
268             sub _sleep {
269 1338     1338   3270 my ($self, @args) = @_;
270 1338 100       3538 if ($self->{no_delay}) {
271 1067         1333 @args = (0);
272             }
273 1338         12745222 return sleep @args;
274             }
275              
276             sub _w00tw00t {
277 5     5   13 my $self = shift;
278 5         32 $self->_dots('Sending PCAP datagrams for fragmentation overlap');
279 5         77 say ' [+] Stack override ***** w00t w00t g0t r00t!';
280 5         41 say '';
281 5         63 print '[';
282 5         14 my $chars = 65;
283 5         28 while ($chars --> 0) {
284 325         4718 print '=';
285 325         2198 $self->_sleep(0.01);
286             }
287 5         110 say ']';
288 5         16 return;
289             }
290              
291             =head1 METHODS
292              
293             =head2 C
294              
295             Run the simulation.
296              
297             =cut
298              
299             sub run {
300 6     6 1 12266 my ($self, $hostname) = @_;
301 6 100       24 unless ($hostname) {
302 1         207 croak('No targets specified.');
303             }
304 5         26 local $| = 1;
305 5         14 print _colored_demon();
306              
307 5         523 $self->_get_ip($hostname);
308 5         61 $self->_launchproxy;
309 5         29 $self->_chainproxies;
310 5         76 $self->_portknock;
311 5         25 $self->_w00tw00t;
312 5         31 $self->_prompt($hostname);
313              
314 5         124 say 'Done';
315 5         94 return;
316             }
317              
318             =head1 AUTHOR
319              
320             Dan Church (h3xxgmxcom)
321              
322             =head1 LICENSE AND COPYRIGHT
323              
324             Copyright (C) 2023 Dan Church.
325              
326             This library is free software; you can redistribute it and/or modify it under
327             the same terms as Perl itself.
328              
329             =head1 AVAILABILITY
330              
331             The latest version of this library is likely to be available from CPAN as well
332             as:
333              
334             L>
335              
336             =head1 THANKS
337              
338             Thanks to janbrennen's L
339             idea|https://github.com/janbrennen/rice/blob/master/hack.exe.c>.
340              
341             =cut
342             1;