Showing posts with label Perl. Show all posts
Showing posts with label Perl. Show all posts

Wednesday, August 24, 2022

Can't locate strict.pm

     Back with another interesting Perl endeavor today. I've been working on a triple DES decryptor and ran across a module, Mcrypt, that supported triple DES through mcrypt. (Please understand that 3DES is broken and shouldn't be used for ANYTHING). Sadly this module isn't currently in Devuan's repos. Never the less I installed all the necessary dependencies (libltdl-dev libmcrypt-dev) to get cpan on my system and then ran cpanm Mcrypt. No issues so far then I try to run some previously working Perl code.

┌─[aut0exec@Devuan]─[~/Projects/Perl]
└──╼ $./Do_Stuff.pl 
Can't locate strict.pm:   /usr/local/share/perl/5.32.1/strict.pm: Permission denied at ./Do_Stuff.pl line 24.

    That was a new one for me. So I jumped into the terminal and took a look in the directory it was complaining about to see if strict.pm was there. While the /usr/local part was already odd to me, I checked things out anyways. To my surprise there wasn't anything in that directory let alone strict.pm. At this point I took to Google and was greeted with the normal lack of useful results but some indications that there might be something wrong with the @INC list of paths for Perl. Reading through Perl's documentation, it was described that one can list out the configured paths in @INC.

┌─[aut0exec@Devuan]─[~/Projects/Perl]
└──╼ $perl -V
Can't locate Config.pm:   /usr/local/share/perl/5.32.1/Config.pm: Permission denied.
BEGIN failed--compilation aborted.

    Umm... What? What in the world did Cpan do!? I kid, this turned out to be a user error but at the time I was frantically searching for a way to fix this system as NONE of my Perl code was working all of a sudden.... except for the root user! So the plot thickens. At this point that glaring Permission denied message started to kick my sysadmin troubleshooting gears in a different direction. perl -V as root shows the following paths as the included paths for Perl to search for modules.

  @INC:
    /etc/perl
    /usr/local/lib/x86_64-linux-gnu/perl/5.32.1
    /usr/local/share/perl/5.32.1
    /usr/lib/x86_64-linux-gnu/perl5/5.32
    /usr/share/perl5
    /usr/lib/x86_64-linux-gnu/perl-base
    /usr/lib/x86_64-linux-gnu/perl/5.32
    /usr/share/perl/5.32
    /usr/local/lib/site_perl

    Interesting so it fails on the third entry for normal users? Wonder what the permissions are!?

┌─[aut0exec@Devuan]─[~/Projects/Perl]
└──╼ $ls -l /usr/local/share/perl/
total 4
drwxr-x--- 7 root root 4096 Aug 24 09:02 5.32.1

    Well well well! That would explain both behaviors I was seeing and since this is folder that is supposed to be accessible to all users of the system, I went ahead and allowed for world to have the ability to read/execute into this directory with chmod o+rx /usr/local/share/perl/5.32.1. Then running perl -V again I was pleasantly surprised to see the expected Perl config output! Now to confirm that my other Perl code was working properly again.

┌─[aut0exec@Devuan]─[~/Projects/Perl]
└──╼ $./Do_Stuff.pl 
[INFO] Doing all the things...

    Whoo! That's what I wanted to see! Turns out that my root user's umask value of 0027 came back to bite me. Since I hadn't installed anything with cpan before, the folder at /usr/local/share/perl/5.32.1 didn't exist so it was created. The umask value however would remove all permissions for the world group upon folder creation though! Seems odd that folder permissions would completely tank the perl interpreter from running but I'm sure there's a good reason from the devs for this behavior. None-the-less, hope this helps anyone else who runs across similar behavior to possibly correct their system.

Thursday, August 11, 2022

Perl Weekly Challenge - 177 Task 2

    This challenge built on a previous week's challenge where individuals needed to locate all the prime numbers up to a certain number. Since that had already been accomplished, it was a lot easier to get started on this challenge!

    First, what's a Palindromic Prime Cyclops number? Well, it's a prime palindrome (number that is the same when read forwards and backawards). For example the number 11 is a prime palindrome. Now what makes this challenge interesting is that the prime palindrome now needs to have a 0 in the middle of the number; in other words the cyclops 'eye' is the zero. While 11 is a prime palindrome, it's not cyclopic, technically no two digit number could fit the bill! This leads to the first possible number that can be a prime cyclopic palindrome being 101, since 101 backwards is..... 101! Note how there is the 'cyclops eye' (zero) in the middle. Shockingly the next value that meets the requirements is the number 16061!

    The challenge luckily only wanted the first 20 prime cyclopic palindromes and provided the list to confirm that user's code produced the correct results. Here is the list:

101, 16061, 31013, 35053, 38083, 73037, 74047, 91019, 94049, 1120211, 1150511, 1160611, 1180811, 1190911, 1250521, 1280821, 1360631, 1390931, 1490941, 1520251

    Let's get started! The first step, I chose to start by filtering out any number that wasn't a palindrome. I specifically started at 3 due to also needing to find prime numbers for later use in the program. I could have and probably should have just pre-made an array of all the primes up to 100 to save some program execution time.

foreach (3..1600000) { 
	if ($_ == reverse($_))

     The above code is pretty simple. The foreach loop simply iterates through the number range 3-1600000. The if statement takes the number, stored in $_, and checks to see if the number is the same forwards and backwards. If it is, then we continue on to the next piece of the challenge, determine if the number is prime. There's a number of ways to do this step.

# If number ends in these it's not prime
my @nums = split(//, $_);
if ( $nums[-1] =~ /0|2|4|5|6|8/) { next; }

# If the sum of the digits is divisble by 3, it's not prime
my $sum = 0;
$sum += $_ for split(//, $_);;
if ( $sum % 3 == 0) { next; }
    The above code rules out a large selection of numbers that aren't prime which helps to reduce the list of palindromes even further. The next step is checking the cyclopic aspect of the number.

my $dig_length = length($_);
if ( 0 == $nums[int((($dig_length/2)))] and 0 ne $dig_length % 2 and check_cyclops $_ and not $_ =~ /0{2,}/ ) { 		
	print ("$_ is a palindromic prime cyclops number!\n"); 
}
    This code does quite a bit. First it checks that middle digit is 0. Then checks that the length of the digit isn't even; even length means that there wouldn't be a zero as the middle digit. Then there is a subroutine called 'check_cyclops'; this will be discussed in a moment. Finally it checks that there aren't multiple zeros in the number. If all of those checks pass, then the program has found a Prime Cyclopic Palindrome!

    Let's revisit the 'check_cyclops' subroutine now. This sub is responsible for ensuring that any value that has made it through all the checks so far is indeed a prime number. Unfortunately to do this the number has to be checked to make sure that it isn't divisible by any other number except 1 and itself. The previous checks were able to get a large portion of the options but there is still a chance that the number is divisible by another prime. So there was also code in this program that builds an array of all the prime numbers up to 10000 while also checking for palindromes. Here is the code to build the array of primes:

if ( $_ < 10000) {
	# Find primes to be used later to check palindromes
	my $i;
	for ( $i=3; $i < $_; $i++) {
		if ( ($_ % $i) == 0 ) { last; }
	}
	if ( $i eq $_ ) { push (@primes, $_); }
  }

    The loop takes the number picked earlier in the foreach loop and attempts to modulo it to every number smaller than it. If the result is ever 0, then that number isn't prime! If the loop makes it all the way to the same number as the number itself, then it is definitely prime and that number gets added to an array of other prime numbers that will be used later to confirm that any potential cyclops values are indeed prime. Finally, 'check_cyclops' is the sub-routine used to confirm the final palindromes are indeed prime.

sub check_cyclops {
	
	my $check_val = shift;
	
	foreach my $prime ( @primes ) {
		if ( $check_val % $prime == 0 ) { return 0; }
	}
	
	return 1;
}

    Time for the moment of truth. Running the program presents the following list of Prime Cyclopic Palindromes: 

aut0exec@aut0exec:~/Projects/Weekly-Challenges$ time ./TWC-177-c1.pl 
101 is a palindromic prime cyclops number!
16061 is a palindromic prime cyclops number!
31013 is a palindromic prime cyclops number!
35053 is a palindromic prime cyclops number!
38083 is a palindromic prime cyclops number!
73037 is a palindromic prime cyclops number!
74047 is a palindromic prime cyclops number!
91019 is a palindromic prime cyclops number!
94049 is a palindromic prime cyclops number!
1120211 is a palindromic prime cyclops number!
1150511 is a palindromic prime cyclops number!
1160611 is a palindromic prime cyclops number!
1180811 is a palindromic prime cyclops number!
1190911 is a palindromic prime cyclops number!
1250521 is a palindromic prime cyclops number!
1280821 is a palindromic prime cyclops number!
1360631 is a palindromic prime cyclops number!
1390931 is a palindromic prime cyclops number!
1490941 is a palindromic prime cyclops number!
1520251 is a palindromic prime cyclops number!
1550551 is a palindromic prime cyclops number!
1580851 is a palindromic prime cyclops number!

real	0m0.811s
user	0m0.807s
sys	0m0.005s
The complete challenge code can be found on my github page. I'm sure there are some better ways to handle the math aspects of this challenge but this worked and was surprisingly quick. I'm looking forward to how other's solved this problem as well.

Saturday, July 23, 2022

Daemonizing a Perl Script with Sysv

    Following up on a previous article in regards to broadcasting UDP data in Perl, here, a need to build a Linux system to run this Perl script at boot time was requested. Simple enough and as with many things in the Perl/Linux world TIMTOWTDI!

    Some background on how all this came about. A door control system was being evaluated and it was determined that it was susceptible to replay attacks. Yes, simply capturing a plaintext broadcasted packet and replaying it would cause this particular door control system to happily lock/unlock doors! Wanting to have a way to train others on how to do this, the messages needed to triggered so that students could capture the messages without requiring both a Window's machine to run the door control software and a person to trigger the controls; Enter Devuan and Perl!

    A quick installation of a minimal Devuan system was performed and then an init script was devised to start the Perl code that would randomly trigger one of four doors periodically. Initially this sounded like it would be far more difficult than it turned out to be and provided a great learning opportunity on how to leverage hash of hashes in Perl (roughly multidimensional arrays in other languages - I'm by no means an expert at why Perl doesn't really have multidimensional array but Gabor Szabo, as usual, has a article on the subject here). None the less, a single multidimensional hash was used to keep track of a number of important things for this door system.

# Four doors allowed by controller
# Status -> Zero = closed, One = open
my %Door_Data = (
  '1' => { 'open' => { 'o_code' => 'f98501', 'o_comm' => '01030303' },
          'close' => { 'c_code' => '796c01', 'c_comm' => '02030303' },
         'status' => 0, 'magic' => '04' }, 
  '2' => { 'open' => { 'o_code' => '853001', 'o_comm' => '03010303' },
          'close' => { 'c_code' => 'e73101', 'c_comm' => '03020303' },
         'status' => 0, 'magic' => '08' }, 
  '3' => { 'open' => { 'o_code' => 'bac601', 'o_comm' => '03030103' },
          'close' => { 'c_code' => 'b9e401', 'c_comm' => '03030203' },
         'status' => 0, 'magic' => '10' }, 
  '4' => { 'open' => { 'o_code' => 'e8ef01', 'o_comm' => '03030301' },
          'close' => { 'c_code' => '90ec01', 'c_comm' => '03030302' },
         'status' => 0, 'magic' => '20' }
  ); 

    So the hash contains a hash which contains a hash; Hash-ception! For anyone interested the rest of the Perl script will be posted on my github since it's not the main purpose of this article. The jist of it is the script simply obtains the network address of the eth0 interface and then randomly chooses a door, determines if the door is opened or closed according to the script (still researching how to determine this from the controller itself), and then determines the proper code and command to send to perform the lock/unlock action against the door.

    Now the process of making the init script to run on Devuan at startup. Launching a Perl program as a daemon on system start-up was not something I'd ever done before but always happy to learn something new. Since this script would rely on a number of system services, getting the dependencies correct would prove to be very important.

#! /bin/sh
### BEGIN INIT INFO
# Provides:          door_lock.pl
# Required-Start:    $local_fs $network $remote_fs $named $time
# Required-Stop:     $local_fs $network $remote_fs $named
# Should-Start:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Launch door unlock/lock script
# Description:       Launch Perl script as system Daemon
### END INIT INFO

    While extensive testing wasn't done on the required system facilities, it was noted that $local_fs and $network were obviously necessary. The next step was to create the required actions. For this start-up script, and most of the ones I create, start|stop|status|restart are all determined to be good options.

    Each of the different actions would leverage start-stop-daemon and since the Perl script doesn't have code to fork on it's own, the --background option for start-stop-daemon will be needed when starting this process at boot (I plan to go back and utilize fork in the Perl code later though). The previous requirements led to a case statement looking like the following.

PATH='/usr/local/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin'
PIDFILE='/run/door_lockd.pid'
DAEMON='/usr/local/sbin/door_control.pl'
NAME='door_lockd'

. /lib/init/vars.sh
. /lib/lsb/init-functions

case "$1" in
  start)
	log_daemon_msg "Starting Door Control daemon" "$NAME" || true
	if start-stop-daemon --start --background --chuid 0:0 -m --pidfile $PIDFILE --exec $DAEMON; then
		log_end_msg 0 || true
	else
		log_end_msg 1 || true
	fi
	;;

  restart)
	log_daemon_msg "Restarting Door Control daemon" "$NAME" || true
	start-stop-daemon --stop --quiet --oknodo --retry TERM/3/KILL/5 --remove-pidfile --pidfile $PIDFILE
	if start-stop-daemon --start --background --chuid 0:0 -m --pidfile $PIDFILE --exec $DAEMON; then
 		log_end_msg 0 || true
	else
		log_end_msg 1 || true
	fi
	;;

  stop)
	log_daemon_msg "Stopping Door Control daemon" "$NAME" || true
	if start-stop-daemon --stop --quiet --oknodo --retry TERM/3/KILL/5 --remove-pidfile --pidfile $PIDFILE ; then
		log_end_msg 0 || true
	else
		log_end_msg 1 || true
	fi
	;;

  status)
	status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $?
	;;
  *)
	echo "Usage: door_lockd.sh [start|stop|restart|status]" >&2
	exit 3
	;;
esac
exit 0

     Thinking everything was good to go at this point, I used update-rc.d <name_of_script> defaults to install the proper symbolic links to the init script in the proper rcX.d directories and rebooted. Sadly upon reboot the Perl program wasn't running but oddly enough there was a PID file and init messages showed that there weren't any errors starting. So the init script definitely ran but there were no signs of whether the Perl program started. Some further reading about the --background argument lead me to wonder if something failed but start-stop-daemon was unable to check the status. Turned out that wasn't the case and the real issue was determined by removing --quiet from the start-stop-daemon and noticing that the Perl code indeed ran but was attempting to open the necessary socket for sending the UDP data before the network adapter was actually ready! So I modifed the Perl program to no longer die on failure to open a socket but rather warn. I also modified the program so that it wouldn't actually try to send data if the socket failed. Oddly, I was under the impression that the $network variable meant that the network adapter would be online and ready to go (ie configured with whatever protocol was needed on that adapter) before init would attempt to run this init script. None the less, the system was rebooted again and low-and-behold the init script kicked off the Perl program as a background daemon and after waiting a little while for the delay in the script to kick out a UDP message, the ether was once again presented with UDP data being broadcasted out to the world.

root@Devuan:~# tcpdump -i eth0 -nn 'port 60000'
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
19:45:17.1920 IP 192.168.1.19.35133 > 255.255.255.255.60000: UDP, length 1172
19:45:23.1926 IP 192.168.1.19.38198 > 255.255.255.255.60000: UDP, length 1172
19:45:25.1932 IP 192.168.1.19.36199 > 255.255.255.255.60000: UDP, length 1172
19:45:27.1937 IP 192.168.1.19.39466 > 255.255.255.255.60000: UDP, length 1172
19:45:32.1945 IP 192.168.1.19.53527 > 255.255.255.255.60000: UDP, length 1172

    Watching the door locks being evaluated was also entertaining as they were randomly locking and unlocking with no one interacting with the control console: Success!

Tuesday, July 12, 2022

Broadcasting UDP data with Perl!

    The other day a scenario presented itself in which a device waited for broadcast traffic to a specific port in order to perform different actions. Horrible security posture aside, this presented an interesting opportunity to practice some sockets in Perl!

#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket;

my $sock = IO::Socket::INET->new(
	Proto => 'udp',
	PeerPort => '5555',
    # Could use network specific broadcast below instead
	PeerAddr => '255.255.255.255',
	LocalAddr => '192.168.1.19',
	Broadcast => 1,
	autoflush => 1,
) or die "Error opening socket: $!\n";

$sock->send('Perl broadcasting to the UDP world!');

    After a quick save and some root privileges, tcpdump presented the following datagram heading out to the ether!


root@aut0exec:~# tcpdump -i any -nn -X 'udp and port 5555'

21:04 eth0  Out IP 192.168.1.19.60735 > 255.255.255.255.5555: UDP, length 35
	0x0000:  4500 003f 44a1 4000 4011 3452 c0a8 0113  E..?D.@.@.4R....
	0x0010:  ffff ffff ed3f 15b3 002b c1f7 5065 726c  .....?...+..Perl
	0x0020:  2062 726f 6164 6361 7374 696e 6720 746f  .broadcasting.to
	0x0030:  2074 6865 2055 4450 2077 6f72 6c64 21    .the.UDP.world!

    From here, I had some other things to do with code for other purposes but none the less, simply getting the broadcast to 255.255.255.255 via UDP was an ordeal. Hopefully, this post can save someone else some troubles later!