Saturday, May 9, 2020

Finding passwords via GDB and strncmp

Recently while mentoring the Rogers Cyber Secure Catalyst Program. A program supported through the generous partnership of the Government of Canada, Rogers Communications and Royal Bank of Canada and executed by Ryerson University in conjunction with the SANS Institute, there was a lab which the candidates had to complete. I completed the lab using a method different from the documentation presented to the students. I did it that way to show there are different ways to solve the same problem. However, one of the students asked if I could instead solve the problem similarly to the lab documentation. In reality I could, but after trying a few tricks and considering time, I figured I would do it and show it to them later.

What was the lab?
Basically, the lab was about finding the password within compiled code, by using GDB/pwndbg.

Here is an idea of the code. This is not the exact code that the lab had but the idea remains the same.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* 
Sample code to compare password
Author: Nik Alleyne
Blog: www.securitynik.com

Compile with debug symbols
$gcc -m32 -ggdb -mpreferred-stack-boundary=2 -o comparePassword comparePassword.c

Compile without debug symbols
$gcc -m32 -mpreferred-stack-boundary=2 -o comparePassword comparePassword.c
*/

#include <stdio.h>
#include <string.h>

// Function to compare password
void compare_string()
    {
        // allocate space for the password
        char USER_PASS[254];
        char PASSWORD[] = "SecurityNik";

        printf("Enter the password: \n");
        
        // read the user's password
        fgets(USER_PASS, sizeof(USER_PASS), stdin);

        // Compare what the user entered with what we expect
        if (strncmp(USER_PASS, PASSWORD, sizeof(PASSWORD)-1) == 0)
                printf("Welcome to my world! :-) \n");
        else
                printf("Stay Out! \n");      
            
    }

int main()
    {
        // Call the compare_string function
        compare_string();
        return 0;
    }

What was missing from above is the "strrev" function, which reverses a string. Anyhow, I don't believe it's absence if of major concern.

The code is compiled as follows:

1
└──╼ $gcc -m32 -mpreferred-stack-boundary=2 -o comparePassword32 comparePassword.c

When this code is run, we get the following:

1
2
3
4
5
┌─[securitynik@securitynik]─[~/c-code]
└──╼ $./comparePassword
Enter the password: 
Testing1
Stay Out! 

Above, an incorrect password was entered.

The challenge at this point is to find the correct password using DBG. The way I solved the challenge was to use the "strings" command along with "rev" command. However, as mentioned above, the students wanted me to solve the problem using GDB/pwndbg.

There are many ways to solve this problem. Here is one:

Load the program into GDB.

1
2
3
4
5
└──╼ $gdb comparePassword32 -q
pwndbg: loaded 187 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from comparePassword32...
(No debugging symbols found in comparePassword32)

Next step, look at the functions being used in the program to understand what the program might be doing.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pwndbg> info functions
All defined functions:

Non-debugging symbols:
0x00001000  _init
0x00001030  fgets@plt
0x00001040  puts@plt
0x00001050  __libc_start_main@plt
0x00001060  strncmp@plt
0x00001070  __cxa_finalize@plt
0x00001080  _start
0x000010c0  __x86.get_pc_thunk.bx
0x000010d0  deregister_tm_clones
0x00001110  register_tm_clones
0x00001160  __do_global_dtors_aux
0x000011b0  frame_dummy
0x000011b5  __x86.get_pc_thunk.dx
0x000011b9  compare_string
0x0000125a  main
0x00001273  __x86.get_pc_thunk.ax
0x00001280  __libc_csu_init
0x000012e0  __libc_csu_fini
0x000012e1  __x86.get_pc_thunk.bp
0x000012e8  _fini

Of greatest importance to us above is the "strncmp" functions. This function can be used to compare two strings and can be considered as the place where the password is being validated.

Next up, we set a breakpoint on our function of interest

1
2
3
4
5
pwndbg> break strncmp
Breakpoint 1 at 0x1060
pwndbg> info breakpoints 
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x00001060 <strncmp@plt>

Now that we have the function, let's run the program by executing the "run" command.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
pwndbg> run
Starting program: /home/securitynik/c-code/comparePassword32 
Enter the password: 
Testing1

Breakpoint 1, 0xf7f2b0b0 in ?? () from /lib32/libc.so.6
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
 EAX  0xffffd13e ◂— 'Testing1\n'
 EBX  0x56559000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x3ef8
 ECX  0x0
 EDX  0xfbad2288
 EDI  0xf7fac000 ◂— 0x1dfd6c
 ESI  0xf7fac000 ◂— 0x1dfd6c
 EBP  0xffffd240 —▸ 0xffffd248 ◂— 0x0
 ESP  0xffffd120 —▸ 0x5655622d (compare_string+116) ◂— add    esp, 0xc
 EIP  0xf7f2b0b0 ◂— push   ebp
───────────────────────────────────[ DISASM ]───────────────────────────────────
  0xf7f2b0b0    push   ebp
   0xf7f2b0b1    mov    edx, dword ptr [esp + 8]
   0xf7f2b0b5    mov    eax, dword ptr [esp + 0xc]
   0xf7f2b0b9    mov    ebp, dword ptr [esp + 0x10]
   0xf7f2b0bd    test   ebp, ebp
   0xf7f2b0bf    je     0xf7f2b2b2
 
   0xf7f2b0c5    mov    cx, dx
   0xf7f2b0c8    and    cx, 0xfff
   0xf7f2b0cd    cmp    cx, 0xff0
   0xf7f2b0d2    ja     0xf7f2b129
 
   0xf7f2b0d4    movdqu xmm2, xmmword ptr [edx]
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000 esp    0xffffd120 —▸ 0x5655622d (compare_string+116) ◂— add    esp, 0xc
01:0004        0xffffd124 —▸ 0xffffd13e ◂— 'Testing1\n'
02:0008        0xffffd128 —▸ 0xffffd132 ◂— 'SecurityNik'
03:000c│        0xffffd12c ◂— 0xb /* '\x0b' */
04:0010        0xffffd130 ◂— 0x6553d000
05:0014        0xffffd134 ◂— 'curityNik'
06:0018        0xffffd138 ◂— 'tyNik'
07:001c│ eax-2  0xffffd13c ◂— 0x6554006b /* 'k' */
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
  f 0 f7f2b0b0
   f 1 5655622d compare_string+116
   f 2 5655626c main+18
   f 3 f7deaef1 __libc_start_main+241
────────────────────────────────────────────────────────────────────────────────
pwndbg> 

Once I entered the invalid password, we then see that GDB/pwndbg reported the string I entered along with the another string below it. Could this other string be the password? Let's test that theory.

Executed the program again, with string which we now find during debugging.


1
2
3
4
└──╼ $./comparePassword32 
Enter the password: 
SecurityNik
Welcome to my world! :-) 

Looks like we are now in. This means the password is "SecurityNik".

Obviously there was lots of testing I had to do to get to this point. However, hopefully the students reading can make sense of what I did.

Using TShark For Continuous Packet Monitoring and Packet Intelligence

If you are already capturing your packets and are trying to figure out how to best use these PCAP files and the data in them, I have released a python package consisting of two scripts to help you gain intelligence from the packets. The primary script "pktIntel.py" retrieves IP addresses, Domains, URLS, http host information along with TLS Server Name Indication information and compares this to data in your existing PCAPs.

To learn more about this package, checkout the project repository on GitHub:  https://github.com/SecurityNik/pktIntel

Sunday, April 5, 2020

From my upcoming Book: Mastering TShark Network Forensics: Remote packet capturing with TShark

In the bonus section of my upcoming book, Mastering TShark Network Forensics, you learn how to perform remote packet capturing. That is, we have a remote computing device where TShark is installed and we would like to perform a capture on the remote device but see and or write the traffic to a local device. Throughout the book, you use “root” on the local machine to execute TShark. However, what happens when you now have a remote device you would like to connect to and for which you are unable to login as “root” to perform your capturing activities.

Perform these actions below on the remote device.

First, on some versions of Linux and if you are using the latest version of Kali, execute the following to reconfigure TShark to allow non-superusers to capture packets.

1
kali@securitynik:~$sudo dpkg-reconfigure wireshark-common

When asked “Should non-superusers be able to capture packets?” select “Yes”.
Add the “kali” user to the “wireshark” group by executing


1
kali@securitynik:~$sudo usermod --append --groups wireshark kali

Then start SSH Server on the remote device using “systemctl” as follows:

1
kali@securitynik:~$ sudo systemctl start ssh

Next verify the SSH server is running by leveraging “
systemctl status ssh


kali@securitynik:~$ sudo systemctl status ssh
 ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; disabled; vendor preset: disabled)
     Active: active (running) since Fri 2020-04-03 22:23:03 EDT; 16min ago
       Docs: man:sshd(8)
             man:sshd_config(5)
    Process: 2011 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
   Main PID: 2012 (sshd)
      Tasks: 1 (limit: 2338)
     Memory: 2.6M
     CGroup: /system.slice/ssh.service
             └─2012 /usr/sbin/sshd D

Now that you know it is running use the “ss” command to verify the service is listening:

1
2
3
kali@securitynik:~$ss --numeric --listen --tcp
State      Recv-Q     Send-Q         Local Address:Port         Peer Address:Port    Process    
LISTEN     0          128               10.0.0.102:22                0.0.0.0:*     

On your local machine
Generate your RSA private and public keys also called your key pair. I will generate this without a passphrase as I’m trying to avoid more administrative overhead.



 
securitynik@SECURITYNIK-SYS:/tmp$ ssh-keygen -C "Created by securitynik@securitynik-sys - used for remote tshark execut
ion" -E sha256 -t rsa -f ~/.ssh/id_tshark
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/securitynik/.ssh/id_tshark.
Your public key has been saved in /home/securitynik/.ssh/id_tshark.pub.
The key fingerprint is:
SHA256:ZnLnKvF4psGdL3dT06GE3X5BECdk7Fa0e0NfSZoOKZs Created by securitynik@securitynik-sys - used for remote tshark exec
ution
The key's randomart image is:
+---[RSA 2048]----+
|            o*++ |
|            o.*oo|
|         . o+o++.|
|          +.o=.++|
|      . SE. o.o+=|
|     ..* +   .o.=|
|      o+o .  . ..|
|      o.=o. o    |
|      .=.o.. .   |
+----[SHA256]-----+


 Verify the certificates have been successfully created

1
2
securitynik@SECURITYNIK-SYS:/tmp$ ls ~/.ssh/id_tshark*
/home/securitynik/.ssh/id_tshark  /home/securitynik/.ssh/id_tshark.pub

Verify the contents of the public key file.

securitynik@SECURITYNIK-SYS:/tmp$ cat ~/.ssh/id_tshark.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDI5wsGheY8+SIQWaFnUB5pNUBy1Z7E6bpY0RHBDw8/vQNzmmrxEj5ImeINBHhtpbClkdyBgzMCRVJbusU
vC+rHdB8BKPialpalERteJ4Ohpj1ChIWibvBac/GrXscUzSPkv42d7j5YISfH7kAHUSqi6uWVjx4Hy8fCrV3cI8QMg85LATVYu5fSsh52GnNLiAoKHp5fzQ
mKvVE56jqKtXHIYU6Q5r9ibpEhdkvgxHlP74DSWJocjoo7miDA6fU6/Q6yucAEt2tNsiZZ+gZhZjhteFTo1H4+SkuJL21wcn0CIE3QlstdIBYjtHU9wXhiH
xNbpKKGzSkTf01CvzRgx61Z Created by securitynik@securitynik-sys - used for remote tshark execution

As everything looks good with the public key, let’s transfer the it to the remote machine using the “ssh-copy-id” command.


securitynik@SECURITYNIK-SYS:/tmp$ ssh-copy-id -i ~/.ssh/id_tshark.pub kali@securitynik
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/securitynik/.ssh/id_tshark.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
kali@securitynik's password:

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'kali@securitynik'"
and check to make sure that only the key(s) you wanted were added.

As we have been asked to attempt to authenticate with “kali@securitynik”, let’s first disable password based authentication, enable and enable “PubkeyAuthentication” in the “/etc/sshd_config”file. This is followed by restarting the SSH service. Once I edited my "/etc/ssh/sshd_config" on the remote device, here is what it looks like:

kali@securitynik:~$ cat /etc/ssh/sshd_config | grep --perl-regexp "^Pubkey|^PasswordA"
PubkeyAuthentication yes
PasswordAuthentication no
Time to restart SSH service

kali@securitynik:~$ sudo systemctl restart ssh
Now that is all set, let’s test our authentication using the keys created previously.

securitynik@SECURITYNIK-SYS:/tmp$ ssh kali@securitynik -i ~/.ssh/id_tshark
Linux securitynik 5.4.0-kali3-amd64 #1 SMP Debian 5.4.13-1kali1 (2020-01-20) x86_64

The programs included with the Kali GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
kali@securitynik:~$

Looks good! You now know that you can authenticate against the remote machine using the key pair

Exit that remote machine by typing “exit” to return to your local machine.
From the local machine now execute.


securitynik@SECURITYNIK-SYS:/tmp$ ssh kali@securitynik -i ~/.ssh/id_tshark 'tshark --interface eth0 -w - ' | tshark --i
nterface - --color

If everything went according to plan, you should now see packets scrolling on your screen.

Here I am rewriting the filter, to capture only ICMP packets on the remote host and writing the contents to a file to a local file while also printing the contents to the local screen.


securitynik@SECURITYNIK-SYS:/tmp$ ssh kali@securitynik -i ~/.ssh/id_tshark 'tshark --interface eth0 -w - -f "icmp"' | t
shark --interface - --color --print -w /tmp/remote_tshark_icmp.pcapng
Capturing on 'Standard input'
Capturing on 'eth0'
    1 0.000000000   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
    2 0.006688350   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
    3 0.013294378   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
    4 0.019760206   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
    5 0.025964669   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
    6 0.037445834   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
    7 0.048449875   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
    8 0.053346311   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
    9 0.060709915   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
   10 0.066094294   10.0.0.100  10.0.0.1     ICMP 76 Echo (ping) request  id=0x0000, seq=0/0, ttl=64
^C10 packets captured

Now analyze the local file as you would any other PCAP. Here I’m analyzing the first record of the file "remote_tshark_icmp.pcapng"


securitynik@SECURITYNIK-SYS:/tmp$ tshark -r  remote_tshark_icmp.pcapng -c 1 -x
0000  0a 00 27 00 00 1e 08 00 27 1f 30 76 08 00 45 00   ..'.....'.0v..E.
0010  00 3e 00 01 00 00 40 01 66 5a 0a 00 00 64 0a 00   .>....@.fZ...d..
0020  00 01 08 00 96 67 00 00 00 00 4d 61 73 74 65 72   .....g....Master
0030  69 6e 67 20 54 53 68 61 72 6b 20 4e 65 74 77 6f   ing TShark Netwo
0040  72 6b 20 46 6f 72 65 6e 73 69 63 73               rk Forensics

Ok, that’s it for how to setup a remote capture. If you wish to learn more, ensure you grab a copy of my book upon its release.


References:
Red Hat Configuring Open SSH
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/s1-ssh-configuration
Stack Overflow - How to capture remote system network traffic




Monday, March 23, 2020

Beginning Deep Learning, Working with the Boston Housing Dataset

This code is all part of my deep learning journey and as always, is being placed here so I can always revisit it as I continue to expand on my learning of this topic.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python3


'''
 Beginning my deep learning journey -
 This part of the journey focus on the binary or two class classification problem.
 Learning to classify the Boston Housing dataset into positive and negative reviews based on 
 text content


 File: dlBoston.py
 Author: Nik Alleyne
 Author Blog: www.securitynik.com
 Date: 2020-02-04
'''

import numpy as np
from keras.datasets import boston_housing
from keras import (models, layers)
from matplotlib import pyplot as plt


def build_model(X_train):
    model = models.Sequential()
    model.add(layers.Dense(64, activation='relu', input_shape=(X_train.shape[1],)))
    model.add(layers.Dense(64, activation='relu'))
    
    '''
    Final layer does not have an activation function defined
    By not specifying an activation function, the model is 
    free to learn and predict the linear values
    '''

    model.add(layers.Dense(1))

    '''
    Mean Squared Error (mse) is widely used in regression problems
    Mean Absolute Error (mae) is used to monitor the absolute value
    between the predictions and the targets
    '''
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    return model




def main():
    # Devide the data into training and testing sets
    (X_train, y_train), (X_test, y_test) = boston_housing.load_data()
    

    # Get the shape of both the training and testing set
    print('\n[*] X_train shape: {}'.format(X_train.shape))
    print('[*] y_train shape: {}'.format(y_train.shape))
    print('[*] X_test shape: {}'.format(X_test.shape))
    print('[*] y_test shape: {}'.format(y_test.shape))

    print('\n[*] Sample records from X_train\n {}'.format(X_train))
    print('\n[*] Sample record from y_train\n {}'.format(y_train))

    mean = X_train.mean(axis=0)
    X_train -= mean
    print('\n[*] X_train after finding the mean \n{}'.format(X_train))

    std_deviation = X_train.std(axis=0)
    X_train /= std_deviation
    print('\n[*] X_train after finding the standard deviation \n{}'.format(X_train))

    X_test -= mean
    X_test /= std_deviation
    print('\n[*] X_test after finding the mean \n{}'.format(X_test))
    

    #Setting up cross validation
    k = 4
    num_validation_samples = len(X_train) // k
    print('[*] Num Validation samples {}'.format(num_validation_samples))
    
    num_epochs = 2
    all_scores = []
    mae_histories = []

    for i in range(k):
        print('[*] Proessing fold: {}'.format(i))
        X_train_val = X_train[i * num_validation_samples: (i + 1) * num_validation_samples]
        y_train_val = y_train[i * num_validation_samples: (i + 1) * num_validation_samples]

        X_train_patial = np.concatenate([X_train[:i * num_validation_samples], X_train[(i+1) * num_validation_samples:]], axis=0)
        y_train_patial = np.concatenate([y_train[:i * num_validation_samples], y_train[(i+1) * num_validation_samples:]], axis=0)
        model = build_model(X_train)
        nn_history = model.fit(X_train_patial, y_train_patial, epochs=num_epochs, batch_size=1, verbose=1)
        val_mse, val_mae = model.evaluate(X_train_val, y_train_val, verbose=1)
        all_scores.append(val_mae)
        
    print('[*] History information from nn_history.history \n{}'.format(nn_history.history))
    mae_histories = nn_history.history['mae']

    print('\n[*] X_train Validation samples\n{}'.format(X_train_val))
    print('\n[*] y_train validation samples\n{}'.format(y_train_val))

    print('[*] All scores \n{}'.format(all_scores))
    print('[*] Here is the mean of all scores {}'.format(np.mean(all_scores)))
    print('[*] Here is the mae scores {}'.format(mae_histories))
    print('[*] Here is the mae mean scores {}'.format(np.mean(mae_histories)))


if __name__ == '__main__':
    main()


'''
References:
https://www.manning.com/books/deep-learning-with-python

'''