GeistHaus
log in · sign up

https://psychocod3r.wordpress.com/feed

rss
10 posts
Polling state
Status active
Last polled May 18, 2026 21:54 UTC
Next poll May 20, 2026 01:24 UTC
Poll interval 86400s
Last-Modified Mon, 27 Oct 2025 18:14:04 GMT

Posts

Apologies for Not Posting
Uncategorizedmeta
Yeah, so, it’s been about four weeks since my last post, even though I was trying to post something new about every week or so. This was entirely unintended. A lot of IRL stuff came up and it’s been taking all my focus lately, and I haven’t had much time for writing. I love doing … Continue reading Apologies for Not Posting →
Show full content

Yeah, so, it’s been about four weeks since my last post, even though I was trying to post something new about every week or so. This was entirely unintended. A lot of IRL stuff came up and it’s been taking all my focus lately, and I haven’t had much time for writing. I love doing this though, and I want to get back to it ASAP.

I have a part-time job now, and I’ve been working 15 hours a week. I’ve also been going to the gym two or three days a week, trying to get back in shape after living a sedentary lifestyle for most of the pandemic. So I’ve been focusing a lot on other goals.

In addition, I’ve also had a lot of life problems that got in the way. The first was when my housemate who’s a total man-child thought it would be really funny to steal my wireless bridge that I was using to connect to the Internet. I had to order another one online and I still haven’t gotten it to work for some reason, so I have to call tech support after the weekend is over to figure out why the new device isn’t showing up on the network, despite the fact that it says it’s connected. Needless to say, it’s been a lot harder to get online lately, and I’ve been using the web mostly through my Android phone, which isn’t exactly ideal for writing.

In the last week, I’ve been dealing with an upper respiratory infection that has affected my cognitive abilities and stamina and forced me to put my job and workout plans on hold (no one wants me coming in to work or the gym when I’m showing symptoms of COVID-19). I think I was pushing myself a little too hard going from a lifestyle where I just sat at home all day to working three days a week and going to the gym three other days and having only Sunday off, a transition that took place within a two or three week time period. It was a very sudden shift, and my system might not have been ready for it, so my immune system was compromised, causing me to get sick.

In any case, I’m going to try to post more consistently moving forward, because I love what I do, and I’ll try not to allow the IRL stuff to get in the way anymore. My man-child housemate has since moved out, and hopefully I can get my network fixed and be back online soon, and my illness seems to be slowly getting better. Hope to see you all soon. 🙂

Processing_art
psychocod3r
http://psychocod3r.wordpress.com/?p=3248
Extensions
Arch Linux pacman – Just the Most Useful Commands
TutorialsUnix and LinuxArch LinuxLinuxpackage managerpacman
Wading through man pages can be a tedious process. Often it’s not clear which options or commands you will be using 99% of the time and which ones are the more obscure options that are only occasionally useful. I was largely mystified by pacman’s man page until I decided to do an exhaustive search through … Continue reading Arch Linux pacman – Just the Most Useful Commands →
Show full content

Wading through man pages can be a tedious process. Often it’s not clear which options or commands you will be using 99% of the time and which ones are the more obscure options that are only occasionally useful. I was largely mystified by pacman’s man page until I decided to do an exhaustive search through it and distill it down to just the bare essentials. That is my goal with this article – to save you the work I had to do by providing a concise list of just the most useful command line options for pacman that you will be using in 99% of real-world cases. I’m going to show you just enough to be dangerous with pacman, but not enough to overwhelm you with superfluous information. The commands are organized by how relevant I expect them to be to a typical Arch user’s life, with more frequently-used commands appearing first, followed by less frequently-used but still valuable commands. Now let’s get started…


pacman -S – Install a package

This is probably the most frequently-used command. pacman commands almost always include a capital letter for a main command followed by optional lowercase letters for subcommands. The -S option to pacman means “Sync”, or “Sync a package with the package repository by downloading and installing it.” Using the -S option by itself simply installs a package and all of its dependencies. For example, to install the Lynx browser, you would use the following command:


$ sudo pacman -S lynx

pacman -Sy – Update the package databases

There are three databases that pacman keeps on your computer so it knows what packages are available. They are core, extra, and community. Before you install any packages, you have to make sure these databases are up-to-date. To do this, use the following command:


$ sudo pacman -Sy

You can also update the databases and install packages in the same command, like this:


$ sudo pacman -Sy lynx

This command will update the package databases and then install the Lynx browser.


pacman -Syu – Universal package upgrade

The -u option to pacman -S tells pacman to do a universal upgrade of all packages on the system. If you want to make sure the entire system is up-to-date without worrying about individual packages, and you’re not concerned about having to wait a while for packages to upgrade, use the command:


$ sudo pacman -Syu

This will upgrade any packages for which more up-to-date versions exist in the package repository. Packages that are already up-to-date will not be upgraded.


pacman -Sl – List all packages in the package repositories

This is in my opinion the most useful pacman function aside from just installing packages. It gives you an exhaustive list of all available packages, organized by repository. You can pipe this command into less and then scroll through the list or search for a specific title to see if you find anything you want to use. The command I typically use it:


$ sudo pacman -Sl | less

pacman -Sw – Download package files without installing anything

If you just want the package files, either so you can examine them or so you can install the package manually, you can use sudo pacman -Sw package to do just the download.

One case where this comes in handy is if you want to export a package to another Linux distro, for example if you have a Debian-based distro and you want to use a package that’s only available in the Arch repositories. Since pacman is a binary-based package manager, you can write a simple script to copy the files to their correct locations on the target system – no compilation necessary – as long as it’s based on the same CPU architecture.


pacman -R – Remove a package

This command is pretty straightforward. It simply removes a package that you have installed. This is the most basic package removal command, removing just the target package without removing any of its dependencies or dependents. To deal with these, use the next two commands…


pacman -Rc – Cascading removal

This removes a package and all of its dependents. That way you can be sure that you don’t leave any so-called “orphan” packages on your system that don’t have the proper dependencies.


pacman -Rs – Reverse cascading removal

This removes a package and any of its dependencies that don’t have other packages depending on them. You can combine this with the previous option to remove a package’s entire family tree of dependencies and dependants. For example:


$ sudo pacman -Rcs lynx

pacman -Ql – List all files included in a package

The -Q option to pacman is used for queries on packages. Most of these queries only work on packages that are currently installed. For queries on the online package repositories, you will typically use -S.

The -l option to pacman -Q lists all files included in a package that is installed on the system. This includes absolute filepaths to any executables, library files, header files, man pages, etc. that that package has installed in your filesystem.


pacman -Qu – List all out-of-date packages

This shows you a list of all packages on your system for which newer versions are available. Since newer versions of packages come out all the time, this list is likely to be fairly long, so you’ll probably want to pipe this into less to view the entire list.


$ sudo pacman -Qu | less

pacman -Qi and pacman -Si – View information about a package

Both these commands show information about a package, such as its version, website URL, license, dependencies, etc. There are a couple subtle differences though. First, pacman -Si shows general information while pacman -Qi shows more detailed information related to a specific installation of a package. Second, pacman -Si queries the package repository by default, whereas pacman -Qi queries installed packages, so if you want information about a package you haven’t installed yet, you’ll have to use pacman -Si.


pacman -U – Upgrade installation

This is very similar to pacman -S, with one subtle difference. pacman -U performs a “clean” upgrade of an existing package, meaning it removes all files for the old version of a package before installing the newer version.


pacman -Qk – Check to see if all files in a package are currently installed

This is the last query command on our list, and it is useful for verifying package integrity, and making sure none of the files used by a package got deleted at any point. If you get a message saying that files were deleted, you should reinstall the package, or use pacman -Sd to download the package and find the missing files and copy just those files to their correct locations.


pacman -Dk and pacman -Dkk – More package integrity checks

The -D option to pacman is for database commands. There are two specific database commands you’re likely to find yourself using. They are pacman -Dk and pacman -Dkk.

pacman -Dk queries the database entry for a package to see if there are any conflicts with other packages.

pacman -Dkk queries the database entry for a package to make sure all dependencies for a package are installed, and it reports any that aren’t.


There are over 75 different pacman commands listed in the pacman man page, but the 17 I listed here should be enough for the vast majority of your use of this package manager. I hope that now you feel ready to tackle just about any package management task in Arch Linux. See you next time.

pacman
psychocod3r
http://psychocod3r.wordpress.com/?p=3095
Extensions
C Program to Plot the Gamma Function in the Terminal
CMathematics and Computer ScienceProgramming and CodingProjects and TinkeringsUnix and LinuxLinuxmathematicsprogrammingUnix
In this post I want to talk about a C program I wrote just recently that plots a mathematical function – specifically the gamma function. I think it’s worth looking at, because this program demonstrates how to handle the various aspects of graphing and plotting using just ASCII characters in the terminal. The program draws … Continue reading C Program to Plot the Gamma Function in the Terminal →
Show full content

In this post I want to talk about a C program I wrote just recently that plots a mathematical function – specifically the gamma function. I think it’s worth looking at, because this program demonstrates how to handle the various aspects of graphing and plotting using just ASCII characters in the terminal. The program draws a pair of axes and then plots the function using asterisks at the proper height for each x value.

The program I have written makes use of libm, which is a library for mathematical functions. A lot of the symbols in math.h and complex.h (including the tgamma function used in my program) are actually defined here, not in libc. So given that the filename of my program is gamma.c, the command to compile it would be:


$ gcc -o gamma-plot gamma.c -lm

The crux of this program is the way it calculates the proper height for each asterisk in the graph based on the parameters given at the command line. These parameters include the width and height of the graph in characters as well as the maximum and minimum values for both x and y. If these are not specified at the command line, default values are selected. An array called graph is used to keep track of the y values. After some error checking, the values in this array are set to integers giving the exact height (row) of each asterisk for each given column. Axes are drawn, and then a graph is plotted in real time using 1/10 second delays between each print operation. A print operation is carried out by using ANSI escape sequences to move the cursor to a specific row and column, then printing an asterisk and flushing the output buffer.

The complete code for the program is as follows:


  1 // Program to plot the gamma function
  2 // in the terminal window
  3 
  4 #include <stdio.h>
  5 #include <unistd.h>
  6 #include <stdlib.h>
  7 #include <string.h>
  8 #include <math.h>
  9 
 10 void usage(){
 11         puts( "Usage:" );
 12         puts( "gamma-plot {key=value}" );
 13         puts( "Possible keys:" );
 14         puts( "\txmin\tMinimum x value" );
 15         puts( "\txmax\tMaximum x value" );
 16         puts( "\tymin\tMinimum y value" );
 17         puts( "\tymax\tMaximum y value" );
 18         puts( "\twidth\tWidth of grid in characters" );
 19         puts( "\theight\tHeight of grid in characters" );
 20 }
 21 
 22 int main( int argc, char **argv ){
 23         int i;
 24         char *key;
 25         char *val;
 26         double xmin = -10, ymin = -10, xmax = 10, ymax = 10;
 27         double xinc, yinc;              // increments for x and y
 28         int width = 80, height = 25;    // width and height of the graph
 29         double xval;                    // raw x value
 30         double *yval;                   // raw y value
 31         int *graph;                     // scaled y values
 32         int xmiddle, ymiddle;           // Coordinates for the center of the screen
 33         // Parse command line parameters:
 34         for( i = 1; i < argc; i++ ){
 35                 if( !strcmp( argv[i], "--help" ) || !strcmp( argv[i], "-h" ) ){
 36                         usage();
 37                         exit( 0 );
 38                 }
 39                 key = strtok( argv[i], "=" );
 40                 val = strtok( NULL, "\0" );
 41                 if( !strcmp( val, "" ) ){ // no = sign in parameter, or = sign at the end
 42                         fprintf( stderr, "%s: Options must be in key=value format.\n", argv[0] );
 43                         exit( -1 );
 44                 }
 45                 else if( !strcmp( key, "xmin" ) )
 46                         xmin = atof( val );
 47                 else if( !strcmp( key, "ymin" ) )
 48                         ymin = atof( val );
 49                 else if( !strcmp( key, "xmax" ) )
 50                         xmax = atof( val );
 51                 else if( !strcmp( key, "ymax" ) )
 52                         ymax = atof( val );
 53                 else if( !strcmp( key, "width" ) )
 54                         width = atof( val );
 55                 else if( !strcmp( key, "height" ) )
 56                         height = atof( val );
 57                 else{
 58                         usage();
 59                         exit( -1 );
 60                 }
 61         }
 62         if( xmin >= xmax || ymin >= ymax || width <= 0 || height <= 0 ){
 63                 fprintf( stderr, "%s: Invalid values for arguments\n", argv[0] );
 64         }
 65         // Populate graph with values:
 66         yval = (double *) calloc( width, sizeof( double ) );
 67         graph = (int *) calloc( width, sizeof( int ) );
 68         xinc = (xmax - xmin) / width;
 69         yinc = (ymax - ymin) / height;
 70         for( i = 0; i < width; i++ ){
 71                 xval = xmin + xinc * i;
 72                 yval[i] = tgamma( xval );
 73                 graph[i] = round( (yval[i] - ymin) / yinc );
 74         }
 75         printf( "\e[2J" ); // Clear screen
 76         fflush( stdout );
 77         // Draw x axis:
 78         ymiddle = round( height / 2 );
 79         for( i = 1; i <= width; i++ )
 80                 printf( "\e[%d;%dH-", ymiddle, i );
 81         // Draw y axis:
 82         xmiddle = round( width / 2 );
 83         for( i = 1; i <= height; i++ )
 84                 printf( "\e[%d;%dH|", i, xmiddle );
 85         // Draw axis decorations:
 86         printf( "\e[%d;%dH^", 1, xmiddle );
 87         printf( "\e[%d;%dHv", height, xmiddle );
 88         printf( "\e[%d;%dH<", ymiddle, 1 );
 89         printf( "\e[%d;%dH>", ymiddle, width );
 90         printf( "\e[%d;%dH+", ymiddle, xmiddle );
 91         fflush( stdout );
 92         usleep( 100000 );
 93         // Plot gamma function:
 94         for( i = 0; i < width; i++ ){
 95                 printf( "\e[%d;%dH", height - graph[i] + 1, i + 1 );
 96                 if( graph[i] >= 0 && graph[i] <= height ) putchar( '*' );
 97                 fflush( stdout );
 98                 usleep( 100000 );
 99         }
100         printf( "\e[%d;%dH", height, width );
101         putchar( '\n' );
102         return 0;
103 }

Here’s a screenshot of the output:

Plotting the gamma function in the terminal

Yeah, I know this was kind of a low-effort post, but bear with me. I’ve been rather busy with real-world stuff lately – trying to get my life back together and on-track now that the pandemic is over, as well as dealing with some medical issues – and I haven’t had much time to focus on tech projects or blogging. I want to continue posting one new article every week though, and hopefully I’ll be able to devote more time to it in the future. Until then, farewell and happy coding.

gamma-plot
psychocod3r
Plotting the gamma function in the terminal
http://psychocod3r.wordpress.com/?p=3226
Extensions
Reverse-Engineering a Linux Process with strace
Projects and TinkeringsReverse EngineeringUnix and LinuxhackingLinuxPOSIXstraceUnix
The pill you took is part of a trace program. It’s designed to disrupt your input/output carrier signals so we can pinpoint your location. And just like that, we can trace signals as they bounce around the Matrix, whether it’s the Matrix that simulates Earth in 1999 or the Matrix that generates pretty pictures on … Continue reading Reverse-Engineering a Linux Process with strace →
Show full content

The pill you took is part of a trace program. It’s designed to disrupt your input/output carrier signals so we can pinpoint your location.

And just like that, we can trace signals as they bounce around the Matrix, whether it’s the Matrix that simulates Earth in 1999 or the Matrix that generates pretty pictures on the screen.

strace is a Linux program that functions much like a batch-mode debugger, tracing all library and system calls made by a process as it runs. It can be very useful for gaining insight into how a Linux program works under the hood, i.e. it’s a form of reverse engineering. I’ve been using it to examine the execution of some programs I have installed, just out of curiosity, and because seeing the proverbial Matrix code dumped to the computer screen gives me a raging mental boner.

To run strace, I’m using the options -r, -f, and -o. -r tells strace to print a relative timestamp for all library calls. This timestamp is relative to the last timestamp printed, not relative to the beginning of execution. -f tells strace to follow all forks and trace any child processes. -o specifies an output file for strace to write its output to. This option is important because if you try to use I/O redirection to send the output to a file, you’ll end up sending the output of the traced program, not of strace itself.

I wanted to see what I got when I traced NMap…


$ sudo strace -rfo strace-nmap.txt nmap workstation

Opening up the output file, we can see the inner workings of an NMap scan:


203287      0.000000 execve("/usr/bin/nmap", ["nmap", "workstation"], 0x7ffffb2b0b58 /* 15 vars */) = 0
203287      0.000564 brk(NULL)          = 0x555afafe7000
203287      0.000227 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd662f7740) = -1 EINVAL (Invalid argument)
203287      0.000577 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
203287      0.000393 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
203287      0.000214 newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=56252, ...}, AT_EMPTY_PATH) = 0
203287      0.000143 mmap(NULL, 56252, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fde37d7a000
203287      0.000082 close(3)           = 0
203287      0.000070 openat(AT_FDCWD, "/usr/lib/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
203287      0.000110 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0  \0\0\0\0\0\0"..., 832) = 832
203287      0.000059 newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=460544, ...}, AT_EMPTY_PATH) = 0
203287      0.000055 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fde37d78000
203287      0.000051 mmap(NULL, 463112, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fde37d06000
203287      0.000042 mmap(0x7fde37d08000, 327680, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fde37d08000
203287      0.000051 mmap(0x7fde37d58000, 122880, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x52000) = 0x7fde37d58000
203287      0.000048 mmap(0x7fde37d76000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6f000) = 0x7fde37d76000
203287      0.000060 close(3)           = 0
203287      0.000042 openat(AT_FDCWD, "/usr/lib/libpcap.so.1", O_RDONLY|O_CLOEXEC) = 3
203287      0.000065 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@`\0\0\0\0\0\0"..., 832) = 832
203287      0.000052 newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=317736, ...}, AT_EMPTY_PATH) = 0
...
203287      0.000116 pselect6(6, [5], NULL, NULL, {tv_sec=0, tv_nsec=2000000}, NULL) = 0 (Timeout)
203287      0.002192 ioctl(3, TIOCGPGRP, 0x7ffd662f63d4) = -1 ENOTTY (Inappropriate ioctl for device)
203287      0.000092 getpgrp()          = 203282
203287      0.000151 pselect6(6, [5], NULL, NULL, {tv_sec=0, tv_nsec=2000000}, NULL) = 0 (Timeout)
203287      0.002243 ioctl(3, TIOCGPGRP, 0x7ffd662f63d4) = -1 ENOTTY (Inappropriate ioctl for device)
203287      0.000095 getpgrp()          = 203282
203287      0.000205 close(4)           = 0
203287      0.000105 setsockopt(5, SOL_PACKET, PACKET_RX_RING, {tp_block_size=0, tp_block_nr=0, tp_frame_size=0, tp_frame_nr=0}, 16) = -1 EBUSY (Device or resource busy)
203287      0.000104 munmap(0x7fde36c77000, 2129920) = 0
203287      0.000251 close(5)           = 0
203287      0.040563 write(1, "Nmap scan report for workstation"..., 50) = 50
203287      0.000319 write(1, "Host is up (0.00039s latency).\n", 31) = 31
203287      0.000368 write(1, "Not shown: 992 filtered ports\n", 30) = 30
203287      0.000297 write(1, "PORT      STATE SERVICE\n135/tcp "..., 216) = 216
203287      0.000125 write(1, "MAC Address: 00:26:B9:B6:1E:7B ("..., 38) = 38
203287      0.000112 write(1, "\n", 1)  = 1
203287      0.000137 write(1, "Nmap done: 1 IP address (1 host "..., 60) = 60
203287      0.000146 close(3)           = 0
203287      0.057997 exit_group(0)      = ?
203287      0.004111 +++ exited with 0 +++

Each line of this file has the following syntax:


PID	timestamp function( arguments ) = return_value

Remember that the PID and timestamp fields are only there because I used the -f and -r options respectively.

A few things I’ve noticed from examining the output file… First of all, arguments to a function are given as exact values, not symbols. If a read call is made, strace will show the literal string that was read from the file. Also, ASCII control codes inside strings are denoted by a backslash and then the ASCII code in octal format. Finally, strace is able to recognize certain numerical codes used in calls and substitute the appropriate macro names for those codes. This makes the trace output much easier to read.

There are a few insights about a program that can be gleaned from looking at this file. For one thing, adding up the timestamps in a program like awk can give you a precise measurement of the execution time, more precise than you would get from running the time command (only problem is there’s no division between user time and system time). The awk script I’ve written to do this is as follows (remember that this only works on files created using the -rf flags):


1 #!/usr/bin/awk -f
2 # Run on output of strace -rfo
3 
4 BEGIN { FS = " "
5         sum = 0 }
6 { sum += $2 }
7 END { print( sum ) }

Another thing you can do is look at which libraries the program uses for its operation. This is shown by the sequences of openat/read/newfstatat/mmap/close calls towards the beginning of execution. For example:


203287      0.000042 openat(AT_FDCWD, "/usr/lib/libpcap.so.1", O_RDONLY|O_CLOEXEC) = 3
203287      0.000065 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@`\0\0\0\0\0\0"..., 832) = 832
203287      0.000052 newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=317736, ...}, AT_EMPTY_PATH) = 0
203287      0.000056 mmap(NULL, 320544, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fde37cb7000
203287      0.000043 mmap(0x7fde37cbd000, 172032, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7fde37cbd000
203287      0.000048 mmap(0x7fde37ce7000, 114688, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x30000) = 0x7fde37ce7000
203287      0.000044 mmap(0x7fde37d03000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x4b000) = 0x7fde37d03000
203287      0.000061 close(3)           = 0

Looking at all of these sequences, I determined that NMap makes use of the following libraries:

  • libc
  • libpcap
  • libm
  • libssh2
  • libssl
  • libcrypto
  • libgcrypt
  • libcre
  • liblua
  • libstdc++
  • libz
  • libzst
  • liblz4
  • liblzma
  • librt
  • libgpg
  • libpthread
  • libnl
  • libdl
  • libdbus
  • libsystemd

Since many of the library functions used by this program are not in my programming vocabulary, I’ve had to do a lot of research on the Arch Linux man pages. Most of the functions actually used in this particular program appear to be from libc, and the other libraries seem to be mostly used for specialized operations specified by different options. Since I used no options and just did a default scan, I don’t expect very many bells and whistles obviously.

One interesting thing that happens shortly after the shared libraries are mapped to memory is the following sequence:


203287      0.000070 openat(AT_FDCWD, "/dev/urandom", O_RDONLY) = 3
203287      0.000115 read(3, "\6\214\6!\20\333\245eA\325&\10\3728\200\1mh\246|\267\320\310\346z\312\21;\232\237\n\224"..., 236) = 236
203287      0.000078 close(3)           = 0

My educated guess is that /dev/urandom is used to generate an order for the port scan, since NMap randomizes the order in which ports are scanned by default. This is very similar to the truly random number generation algorithm I designed, which I talked about in this article. In that algorithm I used /dev/random instead of /dev/urandom.

The next highlight is the following code:


203287      0.000038 stat("/root/.nmap/nmap-services", 0x7ffd662f5800) = -1 ENOENT (No such file or directory)
203287      0.000048 getuid()           = 0
203287      0.000032 geteuid()          = 0
203287      0.000032 readlink("/proc/self/exe", "/usr/bin/nmap", 1024) = 13
203287      0.000088 stat("/usr/bin/nmap-services", 0x7ffd662f5800) = -1 ENOENT (No such file or directory)
203287      0.000049 stat("/usr/bin/../share/nmap/nmap-services", {st_mode=S_IFREG|0644, st_size=1001818, ...}) = 0
203287      0.000057 access("/usr/bin/../share/nmap/nmap-services", R_OK) = 0
203287      0.000046 stat("./nmap-services", 0x7ffd662f5800) = -1 ENOENT (No such file or directory)
203287      0.000047 openat(AT_FDCWD, "/usr/bin/../share/nmap/nmap-services", O_RDONLY) = 3
203287      0.000053 newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=1001818, ...}, AT_EMPTY_PATH) = 0
203287      0.000047 read(3, "# THIS FILE IS GENERATED AUTOMAT"..., 1024) = 1024
203287      0.000043 read(3, "Fields in this file are: Service"..., 1024) = 1024
203287      0.000101 read(3, "he Day\nqotd\t17/udp\t0.009209\t# Qu"..., 1024) = 1024
203287      0.000077 read(3, "\t0.001285\t# Simple Mail Transfer"..., 1024) = 1024
203287      0.000079 read(3, "t Name Server\nwhois\t43/tcp\t0.000"..., 1024) = 1024
203287      0.000076 read(3, "/udp\t0.213496\t# Domain Name Serv"..., 1024) = 1024
203287      0.000067 read(3, "\t# TACACS-Database Service\ntacac"..., 1024) = 1024
203287      0.000065 read(3, "t service\npriv-dial\t75/udp\t0.000"..., 1024) = 1024
203287      0.000068 read(3, ".000824\t# Micro Focus Cobol\npriv"..., 1024) = 1024
203287      0.000065 read(3, "rotocol\nswift-rvf\t97/udp\t0.00036"..., 1024) = 1024
203287      0.000062 read(3, "ora compatible PW changer | 3COM"..., 1024) = 1024
203287      0.000064 read(3, "p\t0.000025\t# Simple File Transfe"..., 1024) = 1024
203287      0.000074 read(3, " | Unisys Unitary Login | NXEdit"..., 1024) = 1024
203287      0.000071 read(3, "025\t# PROFILE Naming System\nprof"..., 1024) = 1024
203287      0.000071 read(3, "UAAC Protocol\niso-tp0\t146/tcp\t0."..., 1024) = 1024
203287      0.000081 read(3, "-routing\t159/udp\t0.000329\nsgmp-t"..., 1024) = 1024
203287      0.000077 read(3, "3/tcp\t0.000013\t# Xyplex\nxyplex-m"..., 1024) = 1024
203287      0.000079 read(3, "ol\naci\t187/tcp\t0.000000\t# Applic"..., 1024) = 1024
203287      0.000074 read(3, ".000025\t# DNSIX Session Mgt Modu"..., 1024) = 1024
203287      0.000072 read(3, "ion\nat-zis\t206/udp\t0.000956\t# Ap"..., 1024) = 1024
203287      0.000072 read(3, "nology License Server\ndbase\t217/"..., 1024) = 1024
203287      0.000077 read(3, "35/tcp\t0.000025\nunknown\t236/tcp\t"..., 1024) = 1024
203287      0.000082 read(3, "ion (set) port | Secure Electron"..., 1024) = 1024
203287      0.000072 read(3, "td-service\t267/udp\t0.000000\t# To"..., 1024) = 1024
203287      0.000079 read(3, "K-BLOCK\nunknown\t288/tcp\t0.000013"..., 1024) = 1024
203287      0.000086 read(3, "0.000346\npkix-timestamp\t318/tcp\t"..., 1024) = 1024
203287      0.000082 read(3, "pawserv\t345/udp\t0.000428\t# Perf "..., 1024) = 1024
203287      0.000079 read(3, "p\t358/udp\t0.000445\ntenebris_nts\t"..., 1024) = 1024
203287      0.000078 read(3, "v | ListProcessor\nulistserv\t372/"..., 1024) = 1024
203287      0.000072 read(3, "llector\t381/udp\t0.000577\t# hp pe"..., 1024) = 1024
203287      0.000072 read(3, "p\t0.000478\nsynotics-relay\t391/tc"..., 1024) = 1024
203287      0.000069 read(3, "99/udp\t0.000395\t# ISO-TSAP Class"..., 1024) = 1024
203287      0.000073 read(3, "ol\nrmt\t411/tcp\t0.000088\t# Remote"..., 1024) = 1024
203287      0.000064 brk(0x555afb04d000) = 0x555afb04d000
203287      0.000057 read(3, "\t424/udp\t0.000610\t# IBM Operatio"..., 1024) = 1024
203287      0.000090 read(3, "1/udp\t0.000395\ncvc_hostd\t442/tcp"..., 1024) = 1024
203287      0.000077 read(3, ".000013\t# Cray SFS config server"..., 1024) = 1024
...

The file the program is reading is basically a version of /etc/services. It tells NMap which services are running on which ports.

Shortly after this, we can see several calls to the socket() function in the Sockets API. Most of the sockets opened are either datagram sockets or Unix domain sockets, though there are a handful of raw sockets as well. It’s difficult to tell what all of them are for. I’m guessing the bulk of the port scans are done using datagram sockets. Here is a sample of the socket code:


203287      0.000116 socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) = 3
203287      0.000086 bind(3, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 0
203287      0.000088 sendmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base={{nlmsg_len=36, nlmsg_type=RTM_GETROUTE, nlmsg_flags=NLM_F_REQUEST, nlmsg_seq=0, nlmsg_pid=0}, {rtm_family=AF_INET, rtm_dst_len=32, rtm_src_len=0, rtm_tos=0, rtm_table=RT_TABLE_UNSPEC, rtm_protocol=RTPROT_UNSPEC, rtm_scope=RT_SCOPE_UNIVERSE, rtm_type=RTN_UNSPEC, rtm_flags=0}, {{nla_len=8, nla_type=RTA_DST}, inet_addr("192.168.10.102")}}, iov_len=36}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 36
203287      0.000414 recvmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base={{nlmsg_len=104, nlmsg_type=RTM_NEWROUTE, nlmsg_flags=0, nlmsg_seq=0, nlmsg_pid=203287}, {rtm_family=AF_INET, rtm_dst_len=32, rtm_src_len=0, rtm_tos=0, rtm_table=RT_TABLE_MAIN, rtm_protocol=RTPROT_UNSPEC, rtm_scope=RT_SCOPE_UNIVERSE, rtm_type=RTN_UNICAST, rtm_flags=RTM_F_CLONED}, [{{nla_len=8, nla_type=RTA_TABLE}, RT_TABLE_MAIN}, {{nla_len=8, nla_type=RTA_DST}, inet_addr("192.168.10.102")}, {{nla_len=8, nla_type=RTA_OIF}, if_nametoindex("enp0s25")}, {{nla_len=8, nla_type=RTA_PREFSRC}, inet_addr("192.168.10.117")}, {{nla_len=8, nla_type=RTA_UID}, 0}, {{nla_len=36, nla_type=RTA_CACHEINFO}, {rta_clntref=4, rta_lastuse=150028827, rta_expires=0, rta_error=0, rta_used=0, rta_id=0, rta_ts=0, rta_tsage=0}}]}, iov_len=512}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 104
203287      0.000218 close(3)           = 0
203287      0.000080 access("/proc/net", R_OK) = 0
203287      0.000102 access("/proc/net/unix", R_OK) = 0
203287      0.000102 socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3
203287      0.000082 ioctl(3, SIOCGIFNAME, {ifr_ifindex=2, ifr_name="enp0s25"}) = 0
203287      0.000092 close(3)           = 0
203287      0.000099 socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
203287      0.000083 setsockopt(3, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0
203287      0.000092 openat(AT_FDCWD, "/proc/net/dev", O_RDONLY) = 4
203287      0.000109 ioctl(3, SIOCGIFCONF, {ifc_len=4192 => 3 * sizeof(struct ifreq), ifc_buf=[{ifr_name="lo", ifr_addr={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}}, {ifr_name="enp0s25", ifr_addr={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("192.168.10.117")}}, {ifr_name="enp0s25", ifr_addr={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("192.168.10.118")}}]}) = 0
203287      0.000113 newfstatat(4, "", {st_mode=S_IFREG|0444, st_size=0, ...}, AT_EMPTY_PATH) = 0
203287      0.000095 read(4, "Inter-|   Receive               "..., 1024) = 572
203287      0.000157 socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 5
203287      0.000085 ioctl(5, SIOCGIFINDEX, {ifr_name="lo", ifr_ifindex=1}) = 0
203287      0.000102 close(5)           = 0
203287      0.000080 ioctl(3, SIOCGIFFLAGS, {ifr_name="lo", ifr_flags=IFF_UP|IFF_LOOPBACK|IFF_RUNNING}) = 0
203287      0.000090 ioctl(3, SIOCGIFMTU, {ifr_name="lo", ifr_mtu=65536}) = 0
203287      0.000086 ioctl(3, SIOCGIFADDR, {ifr_name="lo", ifr_addr={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}}) = 0
203287      0.000091 ioctl(3, SIOCGIFNETMASK, {ifr_name="lo", ifr_netmask={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("255.0.0.0")}}) = 0
203287      0.000112 openat(AT_FDCWD, "/proc/net/if_inet6", O_RDONLY) = 5
203287      0.000119 newfstatat(5, "", {st_mode=S_IFREG|0444, st_size=0, ...}, AT_EMPTY_PATH) = 0
203287      0.000098 read(5, "fe80000000000000021f16fffe3821a4"..., 1024) = 108
203287      0.000115 read(5, "", 1024)  = 0
203287      0.000073 close(5)           = 0

Shortly after this we can see the actual scans taking place, where ports are scanned in an arbitrary order:


203287      0.000282 sendto(4, "E\0\0,5\335\0\0/\6\277\303\300\250\nu\300\250\nf\357\30\0Px\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("192.168.10.102")}, 16) = 44
203287      0.000235 sendto(4, "E\0\0,\32\213\0\0:\6\320\25\300\250\nu\300\250\nf\357\30\4\1x\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("192.168.10.102")}, 16) = 44
203287      0.000174 sendto(4, "E\0\0,GK\0\0002\6\253U\300\250\nu\300\250\nf\357\30\0qx\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(113), sin_addr=inet_addr("192.168.10.102")}, 16) = 44
203287      0.000220 sendto(4, "E\0\0,b[\0\0)\6\231E\300\250\nu\300\250\nf\357\30\37\220x\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("192.168.10.102")}, 16) = 44
203287      0.000208 sendto(4, "E\0\0,6\2\0\0,\6\302\236\300\250\nu\300\250\nf\357\30\1\0x\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(256), sin_addr=inet_addr("192.168.10.102")}, 16) = 44
203287      0.000205 sendto(4, "E\0\0,\323\364\0\0,\6$\254\300\250\nu\300\250\nf\357\30\0\207x\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(135), sin_addr=inet_addr("192.168.10.102")}, 16) = 44
203287      0.000204 sendto(4, "E\0\0,1\323\0\0006\6\274\315\300\250\nu\300\250\nf\357\30\0\26x\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(22), sin_addr=inet_addr("192.168.10.102")}, 16) = 44
203287      0.000223 sendto(4, "E\0\0,\325\346\0\0.\6 \272\300\250\nu\300\250\nf\357\30\1\275x\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(445), sin_addr=inet_addr("192.168.10.102")}, 16) = 44
203287      0.000330 sendto(4, "E\0\0,\257\215\0\09\6<\23\300\250\nu\300\250\nf\357\30\r=x\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(3389), sin_addr=inet_addr("192.168.10.102")}, 16) = 44
203287      0.000326 sendto(4, "E\0\0,i\345\0\0002\6\210\273\300\250\nu\300\250\nf\357\30\0\307x\217+\217\0\0\0\0"..., 44, 0, {sa_family=AF_INET, sin_port=htons(199), sin_addr=inet_addr("192.168.10.102")}, 16) = 44

The program continues like this for most of the rest of its execution, with some recvfrom() and pselect6() functions used as well. According to the man page, pselect6 is a variation of select, which is used by a program to monitor multiple file descriptors at once. Here the multiple file descriptors would be the different sockets it’s using to communicate with the target, where it’s waiting for a response on each of those sockets.

After all the scan signals have been sent out and messages received on any open ports, the program will receive a series of timeouts from ports that do not have services running on them (which will be the vast majority of them):


203287      0.000103 pselect6(6, [5], NULL, NULL, {tv_sec=0, tv_nsec=2000000}, NULL) = 0 (Timeout)
203287      0.002192 ioctl(3, TIOCGPGRP, 0x7ffd662f63d4) = -1 ENOTTY (Inappropriate ioctl for device)
203287      0.000091 getpgrp()          = 203282
203287      0.000103 pselect6(6, [5], NULL, NULL, {tv_sec=0, tv_nsec=2000000}, NULL) = 0 (Timeout)
203287      0.002192 ioctl(3, TIOCGPGRP, 0x7ffd662f63d4) = -1 ENOTTY (Inappropriate ioctl for device)
203287      0.000092 getpgrp()          = 203282
203287      0.000104 pselect6(6, [5], NULL, NULL, {tv_sec=0, tv_nsec=2000000}, NULL) = 0 (Timeout)
203287      0.002189 ioctl(3, TIOCGPGRP, 0x7ffd662f63d4) = -1 ENOTTY (Inappropriate ioctl for device)
203287      0.000093 getpgrp()          = 203282
203287      0.000102 pselect6(6, [5], NULL, NULL, {tv_sec=0, tv_nsec=2000000}, NULL) = 0 (Timeout)
203287      0.002192 ioctl(3, TIOCGPGRP, 0x7ffd662f63d4) = -1 ENOTTY (Inappropriate ioctl for device)
203287      0.000092 getpgrp()          = 203282
203287      0.000116 pselect6(6, [5], NULL, NULL, {tv_sec=0, tv_nsec=2000000}, NULL) = 0 (Timeout)
203287      0.002192 ioctl(3, TIOCGPGRP, 0x7ffd662f63d4) = -1 ENOTTY (Inappropriate ioctl for device)
203287      0.000092 getpgrp()          = 203282
203287      0.000151 pselect6(6, [5], NULL, NULL, {tv_sec=0, tv_nsec=2000000}, NULL) = 0 (Timeout)
203287      0.002243 ioctl(3, TIOCGPGRP, 0x7ffd662f63d4) = -1 ENOTTY (Inappropriate ioctl for device)
203287      0.000095 getpgrp()          = 203282

The final part of the program is when it prints the scan report and then exits with a status of 0:


203287      0.040563 write(1, "Nmap scan report for workstation"..., 50) = 50
203287      0.000319 write(1, "Host is up (0.00039s latency).\n", 31) = 31
203287      0.000368 write(1, "Not shown: 992 filtered ports\n", 30) = 30
203287      0.000297 write(1, "PORT      STATE SERVICE\n135/tcp "..., 216) = 216
203287      0.000125 write(1, "MAC Address: 00:26:B9:B6:1E:7B ("..., 38) = 38
203287      0.000112 write(1, "\n", 1)  = 1
203287      0.000137 write(1, "Nmap done: 1 IP address (1 host "..., 60) = 60
203287      0.000146 close(3)           = 0
203287      0.057997 exit_group(0)      = ?
203287      0.004111 +++ exited with 0 +++

And that, in essence, is the execution of a basic port scan, traced from start to finish. I think I like strace. 🙂

strace_title
psychocod3r
http://psychocod3r.wordpress.com/?p=3206
Extensions
C Program to Generate a Blank MS-DOS Floppy Image in Linux
CProgramming and CodingProjects and TinkeringsRetrocomputingUnix and LinuxDOSLinuxMS-DOSPOSIXprogrammingUnix
How do you do, fellow old-timers? Today’s post is going to be a bit of a crossover, because I’m going to get into some programming stuff, some Linux stuff, and some DOS stuff all at the same time. I’m going to show you a program that I wrote a while back that uses POSIX API … Continue reading C Program to Generate a Blank MS-DOS Floppy Image in Linux →
Show full content

How do you do, fellow old-timers? Today’s post is going to be a bit of a crossover, because I’m going to get into some programming stuff, some Linux stuff, and some DOS stuff all at the same time. I’m going to show you a program that I wrote a while back that uses POSIX API functions to create a blank formatted floppy disk image for VirtualBox VMs running MS-DOS

Of course you can always use Linux’s mkfs.msdos command to do this, but I wanted a cross-platform solution that would work on any POSIX system (keep in mind I was using MacOS at the time). So I wrote my own version of mkfs.msdos that I call mkfloppy.

The floppy generator I have written actually consists of two parts: The first part – mkfloppy – creates a blank file where all bytes are zero. The second part – dosformat – formats this file as a floppy disk image by writing all the FAT filesystem metadata to it. This is information that I copied directly from an existing blank floppy image into a raw binary data file, which is to be read from and then copied onto the first 512 bytes of the new floppy image by dosformat.

The code for mkfloppy is as follows:


 1 #include <stdlib.h>
 2 #include <unistd.h>
 3 #include <errno.h>
 4 #include <string.h>
 5 #include <sys/wait.h>
 6 
 7 #define waitforchild()\
 8 int statloc;\
 9 wait( &statloc );\
10 if( statloc != 0 ) return statloc
11 
12 int main( int argc, char **argv ){
13         pid_t pid;
14         // Build dd command:
15         char cmd[255];
16         strcpy( cmd, "dd if=/dev/zero of=" );
17         strcat( cmd, argv[1] );
18         strcat( cmd, " bs=1024 count=1440" );
19         // First fork-exec is for dd
20         if( (pid = fork()) < 0 ){
21                 write( 2, "Fork error!\n", 12 );
22                 return errno;
23         }
24         else if( pid == 0 ){
25                 system( cmd );
26         }
27         else{
28                 waitforchild();
29         }
30         // Second fork-exec is for dosformat
31         if( (pid = fork()) < 0 ){
32                 write( 2, "Fork error!\n", 12 );
33                 return errno;
34         }
35         else if( pid == 0 ){
36                 execl( "/usr/local/bin/dosformat", "dosformat", argv[1], (char *) 0 );
37         }
38         else{
39                 waitforchild();
40         }
41         return 0;
42 }

This program is fairly easy to understand. All it is is a frontend for dd that executes the following command:


$ dd if=/dev/zero of=arg bs=1024 count=1440

It then forks into the dosformat program, which has the following code:


 1 // This module formats a floppy image with an MS-DOS
 2 // FAT filesystem.
 3 
 4 #include <unistd.h>
 5 #include <fcntl.h>
 6 
 7 int main( int argc, char **argv ){
 8         int in = open( "/usr/local/bin/format.bin", O_RDONLY );
 9         int out = open( argv[1], O_RDWR );
10         char c;
11         for( int i = 0; i < 512; i++ ){
12         // Copy binary data from format table
13                 read( in, &c, 1 );
14                 write( out, &c, 1 );
15         }
16         close( in );
17         char str[4];
18         str[0] = 0xf0; str[1] = str[2] = 0xff; str[3] = '\0';
19         write( out, str, 3 );
20         lseek( out, 0x1400, SEEK_SET );
21         write( out, str, 3 );
22         lseek( out, 0x4200, SEEK_SET );
23         c = 0xf6;
24         while( lseek( out, 0, SEEK_CUR ) != 1024 * 1440 ){
25                 write( out, &c, 1 );
26         }
27         close( out );
28         return 0;
29 }

This program reads bytes one-by-one from the data file format.bin, which is basically just a binary copy of the FAT filesystem’s boot record, and then it writes that data to the new floppy image. It then makes a few other changes later on in the file based on byte patterns that I observed while taking a hexdump of the existing floppy image.

You can download a copy of format.bin here. I had to change the extension to .doc so that WordPress would let me upload it, so you’ll need to change it back to .bin if you want to use it. (No, it’s not a virus, I promise. You can view the file in hexdump if you don’t believe me. 🙂 )

To run this program, you would compile both mkfloppy and dosformat to executable files with those same names, get the format.bin file and copy all three files to /usr/local/bin, then enter the following command:


$ mkfloppy target.img

After I’ve generated the blank floppy image, I can then use a hack to create a floppy image for a directory without having to use a paid program like WinImage or MagicISO. Basically, this hack involves installing an optical media driver in my DOS VM if I haven’t already, then using any open source ISO generator program to create an ISO file from the target folder, then inserting the ISO file into the virtual optical drive of the VM and the blank floppy image into the virtual floppy drive, then going into the DOS VM and using xcopy /s to copy the contents of the D: drive to the A: drive.

Yeah, basically there are no open source programs for creating floppy images from directories, but the fact that there are equivalent programs for ISO’s means you can use this little loophole. Some people may find it easier to just pay to install a proprietary floppy image generator, but I’m a cheapskate who goes out of his way to avoid paying for things, so that’s why I employ hacks like this.

So there you have it, my entire cross-platform system for creating DOS floppy images from directories without paying for any additional software. Hope you enjoyed this post. See you next time.

TDK-Floppy-Disks-in-The-Matrix
psychocod3r
http://psychocod3r.wordpress.com/?p=3150
Extensions
NMap Experiment: Using a Banner Grabbing Attack to Bypass a Load Balancer
Cyber Security and Penetration TestingNetworkingOffensive SecurityProjects and TinkeringsexploitshackingNMappenetration testingserver
Disclaimer: This article talks about port scanning and banner grabbing in NMap, which can be used as precursors to an online attack. This information is for educational and entertainment purposes only and is not to be used for illegal hacking purposes. Disclosing vulnerabilities and exploits is protected by free speech laws, but using such information … Continue reading NMap Experiment: Using a Banner Grabbing Attack to Bypass a Load Balancer →
Show full content
Disclaimer: This article talks about port scanning and banner grabbing in NMap, which can be used as precursors to an online attack. This information is for educational and entertainment purposes only and is not to be used for illegal hacking purposes. Disclosing vulnerabilities and exploits is protected by free speech laws, but using such information maliciously is not protected. I am not responsible for any illegal hacking activities you decide to engage in using this information, nor am I responsible for any damages caused by the malicious use of this information. Since I did manage to find possible vulnerabilities in some servers, all domain names, IP addresses, and company names for my targets have been redacted to prevent people from using this information to attack the companies in question. Please don’t hack anyone without their express permission. Thank you.

Hacker’s Log, Cyberdate 2021-06-07: We have encountered an obstacle while attempting to scan the Acme system for signs of life. A reverse proxy is standing in our way, preventing us from doing a proper analysis of the target system. We are now faced with the task of getting past the proxy so we can learn more about the system in question.

Yeah, I think I’m gonna do an intro like that for all my hacking-related posts moving forward. In any case, I’m back at it again, attempting to boldly go where I’m not invited. Not trying to steal company secrets or anything, I just do this shit because I can, and because I’m bored. 😛
While trying to do an NMap port scan on a website whose name will not be mentioned because I don’t want to get sued if anything bad happens, I encountered a load balancer blocking my path. I was testing various banner grabbing methods, just to see how well they worked. In an attempt to determine what kind of web server they were using, I scanned the target system with this command:


$ sudo nmap -sS -A -p 443 acme.com

And this is the information I got back from NMap:


Starting Nmap 7.91 ( https://nmap.org ) at 2021-06-06 12:13 UTC
Nmap scan report for acme.com (313.56.237.45)
Host is up (0.031s latency).
Other addresses for acme.com (not scanned): 376.39.123.805 54.299.381.85
rDNS record for 313.56.237.45: console-us-standard.console.acme.com

PORT    STATE SERVICE   VERSION
443/tcp open  ssl/https Server
| fingerprint-strings: 
|   FourOhFourRequest, GetRequest, HTTPOptions: 
|     HTTP/1.1 400 Bad Request
|     Server: Server
|     Date: Sun, 06 Jun 2021 17:18:34 GMT
|     Content-Type: text/html
|     Content-Length: 71
|     Connection: close
|     ETag: "60a428a0-47"
|     <!DOCTYPE html><html><head><title>x</title></head><body></body></html>
|   RPCCheck: 
|     HTTP/1.1 500 Internal Server Error
|     Server: Server
|     Date: Sun, 06 Jun 2021 17:18:40 GMT
|     Content-Type: text/html
|     Content-Length: 187
|     Connection: close
|     <html>
|     <head><title>500 Internal Server Error</title></head>
|     <body bgcolor="white">
|     <center><h1>500 Internal Server Error</h1></center>
|     <hr><center>Server</center>
|     </body>
|     </html>
|   RTSPRequest: 
|     <html>
|     <head><title>500 Internal Server Error</title></head>
|     <body bgcolor="white">
|     <center><h1>500 Internal Server Error</h1></center>
|     <hr><center>Server</center>
|     </body>
|     </html>
|   tor-versions: 
|     HTTP/1.1 500 Internal Server Error
|     Server: Server
|     Date: Sun, 06 Jun 2021 17:18:34 GMT
|     Content-Type: text/html
|     Content-Length: 187
|     Connection: close
|     <html>
|     <head><title>500 Internal Server Error</title></head>
|     <body bgcolor="white">
|     <center><h1>500 Internal Server Error</h1></center>
|     <hr><center>Server</center>
|     </body>
|_    </html>
|_http-server-header: Server
|_http-title: 400 The plain HTTP request was sent to HTTPS port
| ssl-cert: Subject: commonName=*.peg.acme.com/organizationName=Acme.com, Inc./stateOrProvinceName=Washington/countryName=US
| Subject Alternative Name: DNS:acme.co.uk, DNS:uedata.acme.co.uk, DNS:www.acme.co.uk, DNS:origin-www.acme.co.uk, DNS:*.peg.acme.com, DNS:acme.com, DNS:acme.com, DNS:uedata.acme.com, DNS:us.acme.com, DNS:www.acme.com, DNS:www.acme.com, DNS:corporate.acme.com, DNS:buybox.acme.com, DNS:iphone.acme.com, DNS:yp.acme.com, DNS:home.acme.com, DNS:origin-www.acme.com, DNS:origin2-www.acme.com, DNS:retail-website.acme.com, DNS:huddles.acme.com, DNS:acme.de, DNS:www.acme.de, DNS:origin-www.acme.de, DNS:acme.co.jp, DNS:acme.jp, DNS:www.acme.jp, DNS:www.acme.co.jp, DNS:origin-www.acme.co.jp, DNS:*.aa.peg.acme.com, DNS:*.ab.peg.acme.com, DNS:*.ac.peg.acme.com, DNS:origin-www.acme.com.au, DNS:www.acme.com.au, DNS:*.bz.peg.acme.com, DNS:acme.com.au, DNS:origin2-www.acme.co.jp
| Not valid before: 2020-11-26T00:00:00
|_Not valid after:  2021-11-24T23:59:59
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
| tls-nextprotoneg: 
|_  http/1.1
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
...
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: load balancer
Running (JUST GUESSING): Citrix embedded (91%)
Aggressive OS guesses: Citrix NetScaler load balancer (91%)
No exact OS matches for host (test conditions non-ideal).

TRACEROUTE (using port 443/tcp)
HOP RTT     ADDRESS
1   2.57 ms DSR-250 (192.168.10.1)
2   ... 30

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 43.63 seconds
Yup, looks like they’re using a load balancer of some sort. Very useful for keeping nosy people like me out, in addition to spreading the load across the network of course.

But wait, what’s that? Looks like the NMap scan shows a bunch of other domain names listed under the Subject Alternative Name header. I did some research on the header and found this page from the load balancer vendor that explains what’s going on. Basically this field is configured by the network administrator when they have a load balancer with several different servers behind it that may fall under different domain names (e.g. example1.com and example2.com as opposed to all of them being under example.com).

Turns out I found a way to use an NMap banner grabbing attack to find the real domain name or IP address of a server that is sitting behind a load balancer or other reverse proxy. Since load balancers serve a security function in addition to their original traffic management function, this banner grabbing attack represents a potential security risk to the organization, and it also gives me the exciting feeling of hacking a large company’s network, even if I’m not trying to cause any harm.
I randomly decided to scan the Australian web server first, just to see what was there. To that end, I ran the following NMap command:


$ sudo nmap -sS -A -p 443 www.acme.com.au

This produced the following output:


Starting Nmap 7.91 ( https://nmap.org ) at 2021-06-06 12:20 UTC
Nmap scan report for www.acme.com.au (504.198.237.93)
Host is up (0.019s latency).
rDNS record for 504.198.237.93: a10-10-19-9.deploy.static.acmetechnologies.com

PORT    STATE SERVICE  VERSION
443/tcp open  ssl/http AkamaiGHost (Akamai's HTTP Acceleration/Mirror service)
| http-methods: 
|_  Potentially risky methods: PUT DELETE TRACE
|_http-server-header: Server
| ssl-cert: Subject: commonName=www.acme.com.au/organizationName=Acme.com, Inc./stateOrProvinceName=Washington/countryName=US
| Subject Alternative Name: DNS:www.acme.com.au, DNS:p-yo-www-acme-com-au.acme.com.au, DNS:p-y3-www-acme-com-au.acme.com.au, DNS:p-nt-www-acme-com-au.acme.com.au, DNS:acme.com.au
| Not valid before: 2020-11-05T00:00:00
|_Not valid after:  2021-10-29T23:59:59
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|   http/1.1
|_  http/1.0
| tls-nextprotoneg: 
|   http/1.1
|_  http/1.0
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Linux 2.6.X|4.X|5.X|3.X (89%)
OS CPE: cpe:/o:linux:linux_kernel:2.6.32 cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:linux:linux_kernel:3.10
Aggressive OS guesses: Linux 2.6.32 (89%), Linux 4.15 - 5.6 (88%), Linux 4.4 (88%), Linux 5.0 - 5.4 (87%), Linux 5.0 - 5.3 (87%), Linux 2.6.32 or 3.10 (86%), Linux 5.4 (85%), Linux 2.6.32 - 2.6.35 (85%), Linux 5.0 (85%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 8 hops

TRACEROUTE (using port 443/tcp)
HOP RTT      ADDRESS
1   2.80 ms  DSR-250 (192.168.10.1)
2   ... 7
8   19.09 ms a10-10-19-9.deploy.static.acmetechnologies.com (504.198.95.88)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 36.54 seconds
So it looks like the server software they’re running is AkamaiGHost. I looked up Akamai and it looks like they’re another firm writing security solutions for businesses. In this case, the Acme company is using a web server package for setting up a mirror server to sit behind a load balancer.

Also, it looks like I struck gold here in terms of vulnerabilities, because it turns out the server implements the PUT, DELETE, and TRACE methods, so someone could conceivably Telnet to their server and just start deleting random files. (Hence why I changed the name of the company to cover my own ass and avoid future lawsuits.)
Wanting to learn more, I did a full port scan of the target system:


$ sudo nmap -sS -A -T2 www.acme.com.au

You might notice that I used the -T2 switch to NMap here. This is to avoid detection by an IDS that could result in the Acme company blocking my IP address. Port scan-based IDS’s like PSAD work by detecting messages being sent to a large number of ports in a short space of time; thus if you deliberately slow down the scan so that the port knocks are spaced further apart, this lowers the probability of detection. (Initially I tried using -T1 but that turned out to be way too slow.)

The result of this scan looks like this:


Starting Nmap 7.91 ( https://nmap.org ) at 2021-06-06 18:19 UTC
Nmap scan report for www.acme.com.au (504.198.237.93)
Host is up (0.036s latency).
rDNS record for 504.198.237.93: server-13-22-20-10.phl50.r.cloud.net
Not shown: 998 filtered ports
PORT    STATE SERVICE  VERSION
80/tcp  open  http     Acme Cloud httpd
|_http-server-header: Cloud
|_http-title: Did not follow redirect to https://www.acme.com.au/
443/tcp open  ssl/http Acme Cloud httpd
| http-cookie-flags: 
|   /: 
|     session-id: 
|       httponly flag not set
|     session-id-time: 
|_      httponly flag not set
| http-methods: 
|_  Potentially risky methods: PUT DELETE TRACE
| http-robots.txt: 82 disallowed entries (15 shown)
| /dp/product-availability/ /dp/rate-this-item/ 
| /exec/acmedos/account-access-login /exec/acmedos/change-style 
| /exec/acmedos/dt/assoc/handle-buy-box /exec/acmedos/flex-sign-in 
| /exec/acmedos/handle-buy-box /exec/acmedos/refer-a-friend-login 
| /exec/acmedos/subst/associates/join /exec/acmedos/subst/marketplace/sell-your-collection.html 
| /exec/acmedos/subst/marketplace/sell-your-stuff.html /exec/acmedos/subst/partners/friends/access.html 
|_/exec/acmedos/tg/cm/member/ /gp/cart /gp/content-form
| http-server-header: 
|   Cloud
|_  Server
|_http-title: Acme.com.au: Buy Acme products
| ssl-cert: Subject: commonName=www.acme.com.au
| Subject Alternative Name: DNS:acme.com.au, DNS:www.acme.com.au, DNS:origin-www.acme.com.au, DNS:www.acme.com, DNS:acme.com, DNS:p-nt-www-acme-com-au.acme.com.au, DNS:p-yo-www-acme-com-au.acme.com.au, DNS:p-y3-www-acme-com-au.acme.com.au
| Not valid before: 2021-03-23T00:00:00
|_Not valid after:  2022-02-24T23:59:59
|_ssl-date: TLS randomness does not represent time
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
No OS matches for host
Network Distance: 21 hops

TRACEROUTE (using port 443/tcp)
HOP RTT      ADDRESS
1   0.75 ms  DSR-250 (192.168.10.1)
2   ... 20
21  23.47 ms server-13-22-20-10.phl50.r.cloud.net (13.294.55.79)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 1018.95 seconds

Turns out Acme is not actually using AkamaiGHost after all. That was just NMap doing some tentative fingerprinting based on the limited information it could glean from a banner grab on the HTTPS port. A banner grab on port 80 shows somewhat more complete information – that Acme is running their own custom web server. Interesting that port 80 provides information about the server software while port 443 doesn’t. This may be another security measure, though I really don’t know.
Out of curiosity, I decided to see what ports were open on the load balancer. I ran the following NMap scan:


$ sudo nmap -sS -T2 acme.com

Which produced the following output:


Starting Nmap 7.91 ( https://nmap.org ) at 2021-06-07 11:47 UTC
Nmap scan report for acme.com (313.56.237.45)
Host is up (0.036s latency).
Other addresses for acme.com (not scanned): 376.39.123.805 54.299.381.85
Not shown: 995 closed ports
PORT    STATE    SERVICE
80/tcp  open     http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
443/tcp open     https
445/tcp filtered microsoft-ds

Nmap done: 1 IP address (1 host up) scanned in 414.82 seconds

Looks like in addition to the standard HTTP ports 80 and 443, the load balancer is also running some Microsoft services, including what looks like NetBIOS and an RPC server. I’m not sure what functions these serve. I tried using Telnet on these ports to do another banner grabbing attack, but I was locked out, so it looks like I won’t be able to get any further information about the server without actually breaking the law. My reconnaissance on the Acme company will have to stop here.
In the future I would like to do more NMap scans on other load balancers to see if I can replicate this banner grabbing attack for other vendors. For now, this is the extent of my research, so I’m going to say farewell, and happy hacking.

dredd-nmap-trailer-screenshot-1589x805
psychocod3r
http://psychocod3r.wordpress.com/?p=3104
Extensions
My (Revised) Step-by-Step Process for Starting Arch Linux Live
Programming and CodingProjects and TinkeringsShell ScriptingUnix and LinuxArch LinuxLinux
In this post I talked about my process for setting up my Arch Linux live system. I still use a live distro (mostly because I don’t want to overwrite any files currently on the hard drive), but in the last few months I’ve made quite a few revisions to my startup process for Arch Linux … Continue reading My (Revised) Step-by-Step Process for Starting Arch Linux Live →
Show full content

In this post I talked about my process for setting up my Arch Linux live system. I still use a live distro (mostly because I don’t want to overwrite any files currently on the hard drive), but in the last few months I’ve made quite a few revisions to my startup process for Arch Linux live. I’ve moved to a newer version after I found that I could no longer install packages since they switched from xz format to zst format. I have this version installed to a USB drive, and I have another (128 GB) USB drive for my swap space that I will extend the RAMdisk into. I’ve also written a lot of scripts to automate the process. In this article I will share my revised process for starting Arch Linux live.

Step 1 of the process is to mount the persistent filesystem:


# mount /dev/sda2 /mnt

Next I run my startup script, which is stored on the persistent filesystem. This is a shell script that automates all the commands I used to do manually:


 1 #!/usr/bin/env bash
 2 # Run as root
 3 
 4 # mount /dev/sda2 /mnt
 5 # cd /mnt/Users/Public/Scripts
 6 # ./startup.sh
 7 echo "Setting console font"
 8 setfont /usr/share/kbd/consolefonts/ter-m28n.psf
 9 echo "Creating user michaelwarren"
10 useradd -ms /usr/bin/zsh michaelwarren
11 echo "Setting password for michaelwarren"
12 passwd -d michaelwarren
13 passwd michaelwarren << END > /dev/null
14 *******
15 *******
16 END
17 echo "Adding michaelwarren to /etc/sudoers"
18 echo "michaelwarren ALL=(root) ALL" >> /etc/sudoers
19 echo "Creating swap space"
20 if [[ $(fdisk -l /dev/sdc | grep "swap") != "" ]]
21 then
22         swapon /dev/sdc1
23 else
24         echo "Error: Swap device is not /dev/sdc1"
25         exit
26 fi
27 echo "Extending RAMdisk"
28 mount -o remount,size=8G /run/archiso/cowspace
29 echo "Connecting to the network"
30 dhclient
31 echo "Copying Vim files"
32 cp -R /mnt/Users/Public/.vim /home/michaelwarren
33 cp /mnt/Users/Public/.vim/_vimrc /home/michaelwarren
34 cp /mnt/Users/Public/.vim/trance.vim /usr/share/vim/vim82/colors
35 chown michaelwarren /home/michaelwarren/*
36 chgrp michaelwarren /home/michaelwarren/*
37 chown michaelwarren /home/michaelwarren/.vim/*
38 chgrp michaelwarren /home/michaelwarren/.vim/*
39 echo "Setting user to michaelwarren"
40 su michaelwarren
41 # sudo ./install-packages.sh

I’m not going to explain what all these commands do because I would just be repeating what I said in the previous post. You can refer to the link at the beginning of this article if you don’t understand anything here.

This script copies a bunch of Vimscript files to the home directory and to ~/.vim. These include the _vimrc file and several syntax files and colorscheme files. My _vimrc file looks like this:


 1 " Vim startup file
 2 
 3 set number
 4 set autoindent
 5 set nowrap
 6 set ruler
 7 
 8 syntax on
 9 
10 " Additional colors:
11 map + :so ~/.vim/syntax.vim<CR>
12 
13 so ~/.vim/peachpuff.vim

I use a modified version of the Peachpuff colorscheme because I want Vim to use yellow instead of brown (I can’t seem to get it to display yellow, even if I set the color to bold). My modified coloscheme file is just peachpuff.vim with all highlighting rules deleted except for the colors I want to change from the default:


 1 " Vim color file
 2 " Maintainer: David Ne\v{c}as (Yeti) <yeti@physics.muni.cz>
 3 " Last Change: 2003-04-23
 4 " URL: http://trific.ath.cx/Ftp/vim/colors/peachpuff.vim
 5 
 6 " This color scheme uses a peachpuff background (what you've expected when it's
 7 " called peachpuff?).
 8 "
 9 " Note: Only GUI colors differ from default, on terminal it's just `light'.
10 
11 " First remove all existing highlighting.
12 
13 let colors_name = "peachpuff"
14 
15 " Colors for syntax highlighting
16 hi Comment term=bold ctermfg=4 guifg=#406090
17 hi Constant term=underline ctermfg=1 guifg=#c00058
18 hi Special term=bold ctermfg=5 guifg=SlateBlue
19 hi Identifier term=underline ctermfg=6 guifg=DarkCyan
20 hi PreProc term=underline ctermfg=5 guifg=Magenta3
21 hi Type term=underline ctermfg=2 gui=bold guifg=SeaGreen
22 hi Ignore cterm=bold ctermfg=7 guifg=bg
23 hi Error term=reverse cterm=bold ctermfg=7 ctermbg=1 gui=bold guifg=White guibg=Red
24 hi Todo term=standout ctermfg=0 ctermbg=3 guifg=Blue guibg=Yellow

The next thing I do is install all my packages. This whole process is now automated as well. I use the following script:


 1 #!/usr/bin/env bash
 2 # Install packages in package database
 3 # Script should be run as superuser
 4 
 5 pacman -Sy
 6 cd /mnt/Users/Public/Scripts
 7 declare -i length=`wc -l packages.txt | cut -d " " -f 1`
 8 for ((i=1; $i<=$length; i++))
 9 do
10         package=`sed -n "${i}p" packages.txt`
11         pacman -S $package << END
12 y
13 END
14 done
15 cd $OLDPWD

To run properly, this script needs a file called packages.txt, which is simply a list of package names. My current packages.txt file looks like this:


alsa-utils
bison
clang
clisp
cmatrix
cmus
cowsay
cpio
ctags
ed
emacs
figlet
finch
flex
gcc
gcc-fortran
gdb
geoip
glibc
gzip
htop
irssi
ltrace
lua
lynx
m4
make
mc
metasploit
nasm
ncurses
neofetch
php
proxychains
remind
ruby
screenfetch
snowball
strace
tcpdump
time
tmux
tree
w3m
wget
whois

I had a problem initially where some of the packages would mess up glibc and other packages wouldn’t work. I tried installing glibc but I got a file signature error, which suggested that I had the wrong PGP public key on file. However, I found that simply installing gcc first and then reinstalling glibc fixed this issue. Therefore I made sure that gcc appears before glibc in the file (which works out pretty well since the order in which they should be installed corresponds to their alphabetical order).

In any case, I now have a much easier time starting up my Arch Linux live distro and don’t have to manually go through a bunch of commands. I just boot up, mount my drive, and run my scripts, and I’m good.

arch-linux-startup
psychocod3r
http://psychocod3r.wordpress.com/?p=3075
Extensions
How to Transfer Files Over SSH on a Linux/Windows Network
NetworkingSystem AdministrationTutorialsUnix and LinuxWindowsLinuxserverSSH
In this tutorial I want to show you how to copy files from one computer to another on a network containing both Linux and Windows hosts, using the SSH protocol. Linux-to-Linux, Linux-to-Windows, and Windows-to-Linux file transfer can be accomplished with an SSH daemon running on one or more Linux hosts and/or a copy of Cygwin … Continue reading How to Transfer Files Over SSH on a Linux/Windows Network →
Show full content

In this tutorial I want to show you how to copy files from one computer to another on a network containing both Linux and Windows hosts, using the SSH protocol. Linux-to-Linux, Linux-to-Windows, and Windows-to-Linux file transfer can be accomplished with an SSH daemon running on one or more Linux hosts and/or a copy of Cygwin running on each Windows host. (For Windows-to-Windows file transfer, it’s much easier to just transfer files over SMB using Windows HomeGroup.) Here are the steps needed to make seamless file transfer over a heterogeneous network a reality:


Step 1: Install OpenSSH if it isn’t already installed

OpenSSH comes preinstalled on most Linux distros, but if it isn’t, you can install it by running the package manager for your distro on the openssh-server package. For example:


$ sudo apt install openssh-server

Step 2: Generate an SSH host key file

Once OpenSSH is installed, you want to generate a host key for authenticating between the client and the server. You do this using the ssh-keygen command.


$ sudo ssh-keygen -A -t ecdsa

In this command, the -A option tells ssh-keygen to create a host key file in /etc/ssh, as opposed to just a file in your home directory. The SSH daemon will not work unless you use a file in the host key format. The -t option specifies what public key cipher is to be used. I’m using ECDSA, which is a digital signature algorithm based on elliptic curve cryptography. You can also use RSA or Diffie-Hellman, but ECDSA seems to be the de facto standard for SSH servers.


Step 3: Start the SSH server

Once you’ve created an SSH host key, you can start the OpenSSH server using the following command:


$ sudo /usr/bin/sshd

This starts up the OpenSSH server. (A lot of Linux systems will complain if you don’t provide the full file path for sshd. I don’t actually know why.) Now you can log into your Linux system remotely from an SSH client.


Step 4: Install Cygwin on Windows

Cygwin is a program for Windows that emulates the POSIX API and command line interface, allowing you to run Unix/Linux software on Windows. It is the method I use to run an SSH client from a Windows computer. You can get the latest version of Cygwin from the Cygwin project’s website. Download the installer and then follow the installation instructions. Cygwin comes with an SSH client installed by default, so you won’t have to do anything special here.


Linux-to-Linux file transfer using scp

The scp command transfers files to a remote host via the SSH protocol. The name is an acronym that stands for “Secure Copy” and it is an encrypted version of the earlier “Remote Copy” program rcp. To do a remote copy using scp, an SSH server must be running on the target machine.

To copy a file from one Linux machine to another using scp, make sure an SSH server is set up on the destination host, then from the source host, type a command like this:


$ scp file michaelwarren@192.168.10.100:~

Let’s dissect this command now… The first argument is the file or files you want to transfer. The second argument is a specification for the destination which has the following structure:


<username> @ <hostname-or-ip-address> : <directory>

When I’m transferring files through scp, my destination username will typically be the main non-root user on the destination machine and my destination directory will typically be that user’s home directory. This is because I often don’t have the proper permissions for other directories on the destination machine such as the OpenMediaVault volume on my Raspberry Pi NAS server. After I do this I go to the destination host (or do a direct SSH login) and move the file to the desired location.

If you want to transfer an entire directory, you add the -r option to scp like so:


$ scp -r dir michaelwarren@192.168.10.100:~

Windows-to-Linux and Linux-to-Windows file transfer

To copy a file from a Windows computer to a Linux computer via SSH, fire up the Cygwin terminal and cd to the directory containing the file you want to transfer. The proper file path to use consists of the full Windows file path with all backslashes replaced with forward slashes, the colon after the drive letter removed, and the entire path preceded with /cygdrive/. For example:

Windows file path:


C:\Users\MichaelWarren\Documents

Cygwin file path:


/cygdrive/c/Users/MichaelWarren/Documents

Once you are in the right directory, use the same scp command you would use for a Linux-to-Linux file transfer.

In addition to uploading files to an SSH server, you can also download files by simply reversing the order of the arguments:


$ scp michaelwarren@192.168.10.100:~/file .

In this command, you use the user@host:path specification for the file on the remote host you want to download, followed by the directory on the local host to copy it to.

Note that in all of these file transfer methods, the scp command is run from the SSH client, not the SSH server.


So that’s basically how you transfer files over SSH on a heterogeneous network. I hope you found this tutorial valuable. If you did, please consider sharing my content on social media, and also checking out some of the other Linux-related articles on my site. For now, farewell and happy command line hacking. 🙂

Transferring files over SSH
psychocod3r
http://psychocod3r.wordpress.com/?p=3040
Extensions
Exploring Borland Turbo Pascal for DOS
PascalProgramming and CodingProjects and TinkeringsRetrocomputingDOSMS-DOSprogramming
I’ve been having some more adventures in VirtualBox, messing around with DOS software. This time I thought I’d explore Borland Turbo Pascal. I have an affinity for ancient programming languages – Pascal, Fortran, BASIC, Lisp, etc. I like them for basically the same reason I like vintage computers and old operating systems. It’s like exploring … Continue reading Exploring Borland Turbo Pascal for DOS →
Show full content

I’ve been having some more adventures in VirtualBox, messing around with DOS software. This time I thought I’d explore Borland Turbo Pascal. I have an affinity for ancient programming languages – Pascal, Fortran, BASIC, Lisp, etc. I like them for basically the same reason I like vintage computers and old operating systems. It’s like exploring ancient ruins from a bygone era, uncovering the mysteries of a distant past. I wasn’t alive back then, so obviously I’m driven more by the sense of mystery than by nostalgia.

Pascal was invented in 1970 by Niklaus Wirth. Wirth is one of the most prolific programming language inventors in history, having created an entire family of languages known as the Wirth languages. These started with Algol W – a spin-off of Algol 60 invented in 1966. Wirth then went on to create Pascal, Modula, Modula 2, Modula 3, and Oberon. These languages are all syntactically similar, and have similar programming constructs and data types.

The structure of Pascal and the other Wirth languages is rather different from the languages we are used to today. It was designed to be lightweight and easy to learn and to encourage structured programming. To that end, each Pascal program is divided into discrete sections devoted to different types of statements. Some of Pascal’s features are relatively unique to the Wirth languages – most notably the presence of set types and set theoretical operations, which I haven’t seen in any other languages.

Borland Turbo Pascal is Borland’s implementation of the Pascal programming language. It includes an editor, compiler, linker, assembler, and debugger. I got my copy from this download page on WinWorldPC.com. The installation looks like basically any other Borland product, though I didn’t need a product key this time.

Installing Borland Turbo Pascal

Installing Borland Turbo Pascal

Installing Borland Turbo Pascal

Readme

I added C:\TP\BIN to my PATH variable and then typed turbo at the command prompt. This brings me to an interface that looks like this:

Starting Borland Turbo Pascal

When I first used this program, I couldn’t figure out why it wasn’t generating executable files when I ran the compile command. Later I realized that there was a certain option I had to toggle in the Compile menu. This menu item allows you to specify whether the compiled binary is to be stored in memory or on the disk (the default is memory).

Compile to memory

Compile to disk

Now we’re ready to write programs and compile them into executable files.

Next thing to do is figure out how to write a basic Hello World program. I started looking at the online documentation for Borland Turbo Pascal, as well as some resources on the web. Borland Turbo Pascal provides ample documentation from the Help menu. It actually provides multiple resources, including a reference manual as well as a guide to the language itself.

Online documentation

Online documentation

A Hello World program in Pascal would look like this:

Hello World program

Now let’s save the file…

Saving a program

Compiling and running the code produces the following output. Success!

Compiling a program

Running a Hello World program

Aaaand for some reason it says “Destination: Memory” in the screenshot. I guess I forgot to change it that time around (I will often go through a process multiple times – once to test it and a second time to get screenshots to post in the Internet). In any case, I did get the binary and managed to run it successfully. 🙂

Next I thought I would write a somewhat more complex (though still rather trivial) program and try out the debugging features in Borland Turbo Pascal. I wrote a program that takes two numbers as input, adds them together, and displays the result:

Debugging

I then went to the Debug menu and started adding breakpoints.

Debugging

Debugging

Debugging

Debugging

Debugging

I set two breakpoints on lines 9 and 10 respectively. Next I went to the Run menu and selected Run.

Debugging

Debugging

The program runs until it reaches the first breakpoint, at which point it stops and returns to the IDE, with the location of the breakpoint selected.

Debugging

Now you can examine various aspects of the program execution at various points in time. You can look at the call stack for example:

Debugging

Debugging

At this point the only thing on the call stack is the main program: Add. Had I set the breakpoint inside a function or procedure, there would be more items on the call stack.

You can also examine the contents of the registers:

Debugging

Debugging

When we’re done examining the current instruction, we can step over to the next instruction and examine that one.

Debugging

Debugging

Now if we run the program again, it starts at the last breakpoint and goes to the next one.

Debugging

Though it turns out this is not actually necessary as we can simply step over all the remaining instructions one by one and examine each one in turn, after which we use the Program Reset command to remove the breakpoints and rewind the program back to the beginning.

So anyway, that’s the basic process of debugging a program in Borland Turbo Pascal. I assume the process is almost identical in other Borland IDE’s such as Borland Turbo C, Turbo C++, Turbo Basic, etc.

Next I thought I’d look at some of the other menus. There are a number of options provided by Borland Turbo Pascal, controlling many different aspects of the software, from the compiler to memory usage to the look and feel.

Compiler options

Memory options

Color preferences

Preferences

Preferences

There’s also an Information command, which shows a dialogue box with all the important information about the program and the current settings:

Information

Moving on to some more programming, I read some more of the tutorials and learned about the CRT library, which is used to produce visual effects on the screen. Libraries in Pascal are called units, and they have the .tpu extension. You can include a TPU file in a Pascal program by using the Uses keyword.

The following program changes the background color to blue and the text color to bright white, then displays the message “Hello, World!” and waits half a second to return to the IDE or prompt.

Using the CRT library

Using the CRT library

Using the CRT library, I wrote a display hack that progressively draws multicolored stars against a black background.

Using the CRT library

Here’s a complete listing of the program for reference:


 1 {Display hack that draws stars on the screen}
 2 Program Sparkle;
 3 Uses Crt;
 4 Var
 5    Counter, X, Y, Ch, Color : Integer;
 6 Begin
 7      Clrscr; {Clear screen}
 8      Textbackground(Black); {Background color}
 9      Randomize; {Seed the randomizer}
10      For Counter := 1 to 1000 do
11      Begin
12           X := Random(80); {X position}
13           Y := Random(25); {Y position}
14           Color := Random(7); {Star color}
15           Ch := Random(4); {Star character}
16           Gotoxy(X,Y); {Move cursor}
17           {Select color:}
18           If Color = 0 Then Textcolor(Red);
19           If Color = 1 Then Textcolor(Green);
20           If Color = 2 Then Textcolor(Blue);
21           If Color = 3 Then Textcolor(Yellow);
22           If Color = 4 Then Textcolor(Cyan);
23           If Color = 5 Then Textcolor(Magenta);
24           If Color = 6 Then Textcolor(White);
25           {Draw star:}
26           If Ch < 2 Then Write(' ');
27           If Ch = 2 Then Write('.');
28           If Ch = 3 Then Write('+');
29           Gotoxy(0,0); {Home cursor}
30           Delay(20); {Delay 20 milliseconds}
31       End;
32 End.

And a screenshot of one frame of the display hack:

Using the CRT library

To round out this article, I want to go through three more Pascal language features that I learned from the tutorials: functions, file handling, and set types.

The following program demonstrates how functions are used in Pascal. Pascal has two kinds of subprograms: functions and procedures. The difference between the two is that functions always have a return value whereas procedures never do. This program is a fairly simple demonstration of calculating the length of the hypotenuse of a triangle using the Pythagorean theorem.


 1 {Calculates the hypotenuse length of a triangle
 2  using the Pythagorean theorem}
 3 Program Py;
 4 Uses Crt;
 5 Var SizeA, SizeB : Real;
 6 Function Pythagorean(A: Real; B: Real) : Real;
 7 Begin
 8      Pythagorean := SQRT( A * A + B * B );
 9 End;
10 
11 Begin
12      Write('Enter the size of Side A: ');
13      Readln(SizeA);
14      Write('Enter the size of Side B: ');
15      Readln(SizeB);
16      Writeln('Side C: ',Pythagorean(SizeA,SizeB));
17      Readkey;
18 End.

The following program demonstrates the use of file handling functions. Like the last two programs it is nontrivial and actually has some practical use. It counts the lines in a file.


 1 {Cbunts lines in a file}
 2 Program LnCount;
 3 Uses Crt;
 4 Var
 5    Count : Integer;
 6    UserFile : Text;
 7    FileName, TLine : String;
 8 Begin
 9      Writeln('Enter filename: ');
10      Readln(Filename);
11      Assign(UserFile,Filename);
12      Reset(UserFile);
13      Count := 0;
14      Repeat
15            Readln(UserFile,TLine);
16            Count := Count + 1;
17      Until Eof(UserFile);
18      Close(UserFile);
19      Writeln('Line count: ',Count);
20      Readkey;
21 End.

Finally, I want to look at set types, because I think these constitute one of the most interesting features of Pascal and the other Wirth languages. Sets are data structures that contain members of a certain type and within a certain range. The range must be restricted to 255 members, as this is the maximum size of a set type in Pascal. The following screenshots show a rather trivial program that demonstrates the operations of union, intersection, and set difference on sets of character values.

Using set types

Using set types

Well, that’s about it for now. See ya.

Borland Turbo Pascal
psychocod3r
Installing Borland Turbo Pascal
Installing Borland Turbo Pascal
Installing Borland Turbo Pascal
Readme
Starting Borland Turbo Pascal
Compile to memory
Compile to disk
Online documentation
http://psychocod3r.wordpress.com/?p=2945
Extensions
Generalized Password Cracking, Part 2: Starting with Some Stock Password Attacks
CCyber Security and Penetration TestingMathematics and Computer ScienceOffensive SecurityProgramming and CodingProjects and TinkeringsalgorithmscryptanalysiscryptographyexploitshackingOpenSSLpassword crackingpenetration testing
Disclaimer: The present series of posts contains information on how to exploit security vulnerabilities in passwords. It is intended for educational and research purposes only. Neither the password cracking tools described in this series nor any of the exploits developed with these tools are to be used for gaining unauthorized access to accounts or other … Continue reading Generalized Password Cracking, Part 2: Starting with Some Stock Password Attacks →
Show full content

Disclaimer: The present series of posts contains information on how to exploit security vulnerabilities in passwords. It is intended for educational and research purposes only. Neither the password cracking tools described in this series nor any of the exploits developed with these tools are to be used for gaining unauthorized access to accounts or other computer resources in the real world. Don’t hack anyone else’s account, computer, device, or network without their express permission. I am not responsible for any illegal hacking activities you decide to engage in using this information, nor am I responsible for any damages that may result from such activities.


Before I implement my password cracking language, I need some stock password attack implementations so I can combine these implementations later into a generalized pattern attack. I have chosen three standard attacks here – a simple dictionary attack that hashes words from a wordlist, a bruteforce attack that searches all passwords up to seven characters in length, and a precomputed table lookup using a file that directly maps hashes back to plaintext passwords.

All of these attacks require using the hashing algorithms we are trying to reverse. These will be supplied by the OpenSSL libraries. There are two libraries that implement the OpenSSL API – they are libssl and libcrypto. Header files for the OpenSSL API can be found in /usr/include/openssl.

To hash passwords, I’m using theSHA1() function declared in openssl/sha.h. This uses a single character for each byte of the hash, meaning if the input is in a hexadecimal format, it will need to be converted into raw byte format so that the result can be compared with the output of the SHA1() function. To do this we can use the following code:


ptr = argv[1];
for( i = 0; i < 20; i++ ){
        image[i] =
        16 *
        ( isdigit( ptr[0] ) ? ptr[0] - '0' : ptr[0] - 'a' + 10 ) +
        ( isdigit( ptr[1] ) ? ptr[1] - '0' : ptr[1] - 'a' + 10 );
        ptr += 2;
}

I also want to display each preimage and the hex code for each hash to the console so that I can see what the password cracker is doing in real time. I realize this makes the program a lot slower, but I just want to get a general idea of what the password cracking process looks like for a given implementation of the algorithm, so I can gain insight into how to improve it. I also want to get a rough idea of how efficient the algorithm is. To produce a hex string for a password hash, we can use the following code:


for( i = 0; i < 20; i++ ){
        printf( "%s%x",
        (uint8_t) hash[i] < 0x10 ? "0" : "",
        (uint8_t) hash[i] );
}

Let’s look at the bruteforce algorithm first. The algorithm I have written is implemented using several layers of nested for loops that select a certain ASCII character between ! and ~ (that’s the entire range of printable ASCII characters). The program concatenates these characters together into a seven-byte string. It then hashes the string consisting of the first byte, then the first two bytes, then the first three bytes, and so on, and compares these to the hash that has been converted to raw-byte format from the argument. If a match is found, it exits, and then you know that the last password printed is a match.

Here is the complete code for the bruteforce program (it’s rather wide, but you can use the sideways scrollbar at the bottom of the code block to view the entire thing):


 1 // Bruteforce attack
 2 
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <stdint.h>
 6 #include <string.h>
 7 #include <ctype.h>
 8 #include <openssl/sha.h>
 9 
10 int main( int argc, char **argv ){
11         char a, b, c, d, e, f, g; // Characters in the password
12         int i, j;
13         char *ptr;
14         char passwd[8];
15         char hash[21];
16         char image[21];
17         // Null-terminate all strings:
18         passwd[7] = '\0';
19         hash[20] = '\0';
20         image[20] = '\0';
21         // Convert from hex to raw bytes:
22         ptr = argv[1];
23         for( i = 0; i < 20; i++ ){
24                 image[i] =
25                 16 *
26                 ( isdigit( ptr[0] ) ? ptr[0] - '0' : ptr[0] - 'a' + 10 ) +
27                 ( isdigit( ptr[1] ) ? ptr[1] - '0' : ptr[1] - 'a' + 10 );
28                 ptr += 2;
29         }
30         for( a = 33; a < 127; a++ ){
31                 for( b = 33; b < 127; b++ ){
32                         for( c = 33; c < 127; c++ ){
33                                 for( d = 33; d < 127; d++ ){
34                                         for( e = 33; e < 127; e++ ){
35                                                 for( f = 33; f < 127; f++ ){
36                                                         for( g = 33; g < 127; g++ ){
37                                                                 passwd[6] = a;
38                                                                 passwd[5] = b;
39                                                                 passwd[4] = c;
40                                                                 passwd[3] = d;
41                                                                 passwd[2] = e;
42                                                                 passwd[1] = f;
43                                                                 passwd[0] = g;
44                                                                 for( i = 1; i <= 7; i++ ){
45                                                                         SHA1( passwd, i, hash );
46                                                                         printf( "SHA1(%s,%d)=", passwd, i );
47                                                                         // Print hash in hex format:
48                                                                         for( j = 0; j < 20; j++ ){
49                                                                                 printf( "%s%x",
50                                                                                          (uint8_t) hash[j] < 0x10 ? "0" : "",
51                                                                                          (uint8_t) hash[j] );
52                                                                         }
53                                                                         putchar( '\n' );
54                                                                         if( !strcmp( hash, image ) ) exit( 0 );
55                                                                         // Stop when reaching the correct preimage.
56                                                                         // The user will know that whichever preimage
57                                                                         // is last is the correct one.
58                                                                 }
59                                                         }
60                                                 }
61                                         }
62                                 }
63                         }
64                 }
65         }
66         return 0;
67 }

As you can see, I had the algorithm place the characters in reverse order. Originally I had it place them in forward order, but then I found that if I did that, the program would spend the first several hours just testing different numbers of exclamation points over and over. If it does them in reverse order, it will cycle through a variety of shorter sequences more quickly, allowing it to catch very weak passwords in a decent amount of time.

The method of generating every possible 7-character string then testing substrings of each string may seem inefficient at first glance since it will end up testing shorter passwords multiple times, but the reality is the code is far less redundant since the alternative would be a long, tedious sequence consisting of one for loop, then two nested for loops, and so on, one after the other. A single nestedfor loop is also much easier to build from a pattern command given by the user. Since these algorithms are intended to be incorporated into an actual programming language implementation, I think this is fairly important.

It’s also not that much more complex, which you can see if you write out the complexity equations for the two algorithms. If we measure complexity by how many hashes we have to compute, then the time complexity of the long, tedious code would look like this:

Brute-forcing algorithm with exponential complexity

On the other hand, the complexity of a single nested loop pattern would look like this:

Brute-forcing algorithm with exponential complexity

In these equations, s represents the set of characters, n represents the maximum length of the passwords searched, and C1 and C2 represent the time complexities of the first and second algorithm respectively. In both cases, the algorithm completes in exponential time, and the complexity moving from the first to the second is merely multiplied by a constant factor. This factor will be approximately n. For example, if we calculate the complexities for |s| = 94 (the number of printing ASCII characters) and n = 7 we get the following result:


C1=65545047154954
C2=453934315934848
C2/C1=6.925532

If we hash the password “A0” using the sha1sum command and then try to reverse this hash using the bruteforce program, we get the following output:


$ ./bruteforce b8c4ed32039755356ab5eaf0878516ef63ab96f8
SHA1(!!!!!!!,1)=0ab8318acaf6e678dd02e2b5c343ed41111b393d
SHA1(!!!!!!!,2)=b4613f8681b1e26686a2e88299525a4dc89c46d5
SHA1(!!!!!!!,3)=9a7b006d203b362c8cef6da001685678fc1d463a
SHA1(!!!!!!!,4)=f6bb43b4c87f68cd820cc911a787b06060e85227
SHA1(!!!!!!!,5)=1227cb28ec9e51942b7dacc0d5453e10d975612f
SHA1(!!!!!!!,6)=bae598184569d68359358ff314765c82166f9dfd
SHA1(!!!!!!!,7)=9b8a410b57694951c5ca9405c741fcc7578af9b1
SHA1("!!!!!!,1)=2ace62c1befa19e3ea37dd52be9f6d508c5163e6
SHA1("!!!!!!,2)=b82a1cbc556a615014aee599d4da44bfcf30db50
SHA1("!!!!!!,3)=013c0f8fccac2124416e8cf9ed8e13fd0439ee50
SHA1("!!!!!!,4)=64171fac81247af7a9373e5e4093acbd3ef429a8
SHA1("!!!!!!,5)=ebf5b260eeeace31fc2d3fe65562c8b04e4d0391
SHA1("!!!!!!,6)=6314f29e8974d0fb4398d2979207f5136b95e755
SHA1("!!!!!!,7)=2b72e4e89f114cbcd4e373cdb1e4fc6eed3b0694
SHA1(#!!!!!!,1)=d08f88df745fa7950b104e4a707a31cfce7b5841
SHA1(#!!!!!!,2)=2d46bc445fe5eef1125228cce271ca63a02b246d
SHA1(#!!!!!!,3)=62835194030ec6d9038a83472408cbebb5734055
SHA1(#!!!!!!,4)=d8e16006a45475bd5dca1de8a6314a6a981cc5bb
SHA1(#!!!!!!,5)=78978e44a63d26fdeca7bdf4624858c6682357f2
SHA1(#!!!!!!,6)=cc23d43e101b56155e1ad5ac7fa61a13bf916faf
...
SHA1(>0!!!!!,4)=4cb2dca31b9c5fe1c4b7a09a89127a64a6db2404
SHA1(>0!!!!!,5)=e58c06cdb3ad914c8254d62fb789e50bc35afa6e
SHA1(>0!!!!!,6)=b6af29586e7e797eb7d5f3f801f5a54ed96bc5e5
SHA1(>0!!!!!,7)=54e3bae94042f95dd8820694fcefb83a30718072
SHA1(?0!!!!!,1)=5bab61eb53176449e25c2c82f172b82cb13ffb9d
SHA1(?0!!!!!,2)=04f46713539ee1e582054f9a5438865d91001599
SHA1(?0!!!!!,3)=b386febe2cb35532c96865158ae4de20fe3b22b8
SHA1(?0!!!!!,4)=e3cebdd92f5459245587e8fadfcf303cca1588ff
SHA1(?0!!!!!,5)=2809994d8a367c043ec3cda0f6487820f15d3eb3
SHA1(?0!!!!!,6)=1a4d1589c9e439e36ddc9446bc077cf1fb16da73
SHA1(?0!!!!!,7)=f6f1a46d0283165333dfb6813ec604abc1bf7fce
SHA1(@0!!!!!,1)=9a78211436f6d425ec38f5c4e02270801f3524f8
SHA1(@0!!!!!,2)=5d535dabfd744109cbbae1ad1b4901d1946e7548
SHA1(@0!!!!!,3)=98a2a246adf8c1f3a4091ab7cdd8a5e72d21b017
SHA1(@0!!!!!,4)=cb5a65be01d4e284003de624b89698877fcfba23
SHA1(@0!!!!!,5)=9cf399810e4e636f765ad79cd4a5eec040f61c63
SHA1(@0!!!!!,6)=bb54c38ed15fb8209c283aaa045c10bb18da89ad
SHA1(@0!!!!!,7)=033e3ebcf47522581b62c94736182c11b5c4fc9b
SHA1(A0!!!!!,1)=6dcd4ce23d88e2ee9568ba546c007c63d9131c1b
SHA1(A0!!!!!,2)=b8c4ed32039755356ab5eaf0878516ef63ab96f8

As you can see, the algorithm stopped once it reached the hash for the password we chose. The second argument in the parenthesis is 2, meaning the algorithm found a match when it tested the first two characters of the sequence.

The program for the dictionary attack has a lot of the same code, though the actual attack is structured differently. The program takes two arguments: the first is the password hash you’re trying to crack and the second is the file path for the wordlist the program is to use. The program is fairly straightforward, reading one word at a time from the wordlist, hashing it, and then comparing it to the raw byte version of the first argument.


 1 // Dictionary attack
 2 
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <stdint.h>
 6 #include <string.h>
 7 #include <errno.h>
 8 #include <ctype.h>
 9 #include <openssl/sha.h>
10 
11 int main( int argc, char **argv ){
12         int c, i, len;
13         FILE *wordlist;
14         char *ptr;
15         char passwd[100];
16         char hash[21];
17         char image[21];
18         // Null-terminate strings:
19         hash[20] = '\0';
20         image[20] = '\0';
21         // Convert from hex to raw bytes:
22         ptr = argv[1];
23         for( i = 0; i < 20; i++ ){
24                 image[i] =
25                 16 *
26                 ( isdigit( ptr[0] ) ? ptr[0] - '0' : ptr[0] - 'a' + 10 ) +
27                 ( isdigit( ptr[1] ) ? ptr[1] - '0' : ptr[1] - 'a' + 10 );
28                 ptr += 2;
29         }
30         if( !(wordlist = fopen( argv[2], "r" )) ){
31                 fprintf( stderr, "%s: Can't open wordlist file: %s\n", argv[0], strerror( errno ) );
32                 exit( errno );
33         }
34         while( (c = fgetc( wordlist )) != EOF ){
35                 ungetc( c, wordlist );
36                 for( i = 0; i < 100; i++ ) passwd[i] = '\0'; // Make sure string is null-terminated
37                 fgets( passwd, 99, wordlist );
38                 // The next three lines get rid of any newline characters at the end.
39                 len = strlen( passwd ) - 1;
40                 if( passwd[len] == '\n' ) passwd[len--] = '\0';
41                 if( passwd[len] == '\r' ) passwd[len--] = '\0';
42                 // Now for the actual attack:
43                 SHA1( passwd, len + 1, hash );
44                 printf( "SHA1(%s)=", passwd );
45                 // Print hash in hex format:
46                 for( i = 0; i < 20; i++ ){
47                         printf( "%s%x",
48                                 (uint8_t) hash[i] < 0x10 ? "0" : "",
49                                 (uint8_t) hash[i] );
50                 }
51                 putchar( '\n' );
52                 if( !strcmp( hash, image ) ){
53                         fclose( wordlist );
54                         exit( 0 );
55                 }
56         }
57         fclose( wordlist );
58         return 0;
59 }

Keep in mind I haven’t completely tested this program. I’ve only tested it using wordlists in the Unix format, so I’m not 100% sure if it works for DOS-format wordlists. Also, the program segfaults if it encounters a blank line. I decided it would be easier to just delete the one blank line present in the John the Ripper wordlist than it would be to modify the program so it could process blank lines.

I’ve run this program using two wordlists: john.txt and cain.txt. john.txt has a little over 3,000 words while cain.txt has a little over 300,000. It took 9 minutes and 51.58 seconds for the program to hash every single password in cain.txt and print the result of each hash to the console. On the other hand, I let my bruteforce password cracker run overnight and it was still running the next morning. So clearly there’s a massive efficiency disparity between bruteforce and dictionary attacks. Dictionary attacks are far more efficient even with very large wordlists.

The final attack is the one using a precomputed table lookup. Originally I was going to do a rainbow table attack, but then I did some research on how rainbow tables work and realized it would be far too complicated for this stage of the project. So the lookup table we will be using is simply a one-to-one mapping of hashes onto passwords.

The lookup method consists of two programs: one to generate the lookup table for a wordlist and another to use the lookup table to quickly find the preimage of a hash. The following code generates the lookup table:


 1 // Precomputed table generator
 2 
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <stdint.h>
 6 #include <string.h>
 7 #include <errno.h>
 8 #include <openssl/sha.h>
 9 
10 #define UNIX 0
11 #define DOS  1
12 
13 int main( int argc, char **argv ){
14         int c, i, len;
15         char passwd[100];
16         char hash[21];
17         hash[20] = '\0';
18         FILE *wordlist;
19         uint8_t format = UNIX;
20         if( !argv[1] ) wordlist = stdin;
21         else{
22                 if( !(wordlist = fopen( argv[1], "r" )) ){
23                         fprintf( stderr, "%s: Can't open wordlist file: %s\n", argv[0], strerror( errno ) );
24                         exit( errno );
25                 }
26         }
27         while( (c = fgetc( wordlist )) != EOF ){
28                 ungetc( c, wordlist );
29                 for( i = 0; i < 100; i++ ) passwd[i] = '\0'; // Make sure string is null-terminated
30                 fgets( passwd, 99, wordlist );
31                 // The next three lines get rid of any newline characters at the end.
32                 len = strlen( passwd ) - 1;
33                 if( passwd[len] == '\n' ) passwd[len--] = '\0';
34                 if( passwd[len] == '\r' ){
35                         passwd[len--] = '\0';
36                         format = DOS;
37                 }
38                 // Now we generate the table entry:
39                 SHA1( passwd, len + 1, hash );
40                 // Print hash in hex format:
41                 for( i = 0; i < 20; i++ ){
42                         printf( "%s%x",
43                                 (uint8_t) hash[i] < 0x10 ? "0" : "",
44                                 (uint8_t) hash[i] );
45                 }
46                 putchar( ':' );
47                 printf( "%s", passwd );
48                 if( format == DOS ) putchar( '\r' );
49                 putchar( '\n' );
50         }
51         if( wordlist != stdin ) fclose( wordlist );
52         return 0;
53 }

This program can either take a wordlist as an argument or read from standard input. It does the latter automatically if no argument is provided. It then reads each line, hashes it, and then prepends the hash to the password, separated by a colon. It uses a binary value to keep track of whether the file it’s reading is in DOS format or Unix format and makes sure its output is in the same format.

Then we have the actual table lookup algorithm. Its code looks like this:


 1 // Precomputed table lookup
 2 
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 #include <ctype.h>
 7 #include <errno.h>
 8 
 9 #define hex_value( c ) (isdigit( c ) ? c - '0' : tolower( c ) - 'a' + 10)
10 
11 int main( int argc, char **argv ){
12         int c, i, len;
13         char *fieldsep;
14         char buf[150];
15         char *hash;
16         char *passwd;
17         FILE *table;
18         len = 1;
19         // Set field separator string:
20         fieldsep = ":";
21         for( i = 1; i < argc; i++ ){
22                 if( argv[i][0] == '-' && argv[i][1] == 'f' ){
23                         fieldsep = argv[i+1];
24                 }
25         }
26         for( i = 0; i < 150; i++ ) buf[i] = '\0';
27         if( !(table = fopen( argv[2], "r" )) ){
28                 fprintf( stderr, "%s: Can't open table file: %s\n", argv[0], strerror( errno ) );
29                 exit( errno );
30         }
31         while( (c = fgetc( table )) != EOF ){
32                 ungetc( c, table );
33                 fgets( buf, 149, table );
34                 hash = strtok( buf, fieldsep );
35                 passwd = strtok( NULL, "\r\n" );
36                 // Test only the prefix:
37                 for( i = 0; i < len; i++ ){
38                         putchar( hash[i] );
39                         if( hex_value( hash[i] ) > hex_value( argv[1][i] ) ){
40                                 putchar( '\n' );
41                                 printf( "Password not found\n" );
42                                 fclose( table );
43                                 exit( 0 );
44                         }
45                         if( hash[i] != argv[1][i] ) break;
46                 }
47                 if( i != len ){
48                         putchar( '\n' );
49                         continue;
50                 }
51                 // Extend the prefix:
52                 while( hash[len] == argv[1][len] ){
53                         if( len < 40 ) putchar( hash[len] );
54                         if( hex_value( hash[len] ) > hex_value( argv[1][len] ) ){
55                                 putchar( '\n' );
56                                 printf( "Password not found\n" );
57                                 fclose( table );
58                                 exit( 0 );
59                         }
60                         len++;
61                 }
62                 putchar( '\n' );
63                 if( len == 41 ){
64                         printf( "%s\n", passwd );
65                         fclose( table );
66                         exit( 0 );
67                 }
68         }
69         printf( "Password not found.\n" );
70         fclose( table );
71         return 0;
72 }

This program is different from the other ones in that it doesn’t need to use any cryptography functions. It is designed to take advantage of a sorted lookup table. This table would be generated using the previous program and then sorted using the Unix sort command. The program keeps track of how many hex digits in the current line match the target hash. For subsequent lines it only checks that many characters. It then attempts to extend the number of matching characters. If any characters don’t match it stops checking. If it finds a digit that gives the hash a greater numerical value than the target, then it knows the target hash is not in the database, so it exits. If it manages to match every character in the hash, it prints the preimage.

Checking only the number of characters known to match is a huge time saver because for the vast majority of hashes only one character has to be checked. Here’s a sample output of this program:


$ ./lookup b892f067921d231448e8f0a591107de8b2ad3202 john.sorted
0
0
0
0
0
0
0
0
0
0
...
b
b
b
b
b
b
b8
b8
b8
b8
b892f067921d231448e8f0a591107de8b2ad3202
sunny

As you can see, only the first character was checked for all but the last five hashes, and then only the first two characters were checked for all but the last hash, which was a match.

If we run the program for a hash that doesn’t have a match, we see a similarly efficient process:


$ ./lookup 73216320b4a1186852ff9f93d7df27954aab126c john.sorted
0
0
0
0
0
0
0
0
0
0
...
7
7
7
7
7
7
7
7
73
732
733
Password not found

Now that I’ve successfully implemented these three stock password attacks, the next step would be to combine the concepts from these algorithms into a pattern attack which can be automated using a pattern like what was shown in the first part of this series. This will be the subject of Part 3, so stay tuned. Until then, farewell and happy hacking.

Generalized Password Cracking
psychocod3r
Brute-forcing algorithm with exponential complexity
Brute-forcing algorithm with exponential complexity
http://psychocod3r.wordpress.com/?p=2808
Extensions