Beginning Notes:
1. A modest amount of core knowledge is assumed.
2. A read of "Smashing The Stack for Fun and Profit" by Aleph One would be recommeded.
3. Point '2' doesn't guarante anything and for further queries refer to point '1' or ask away under comments.
To get the # symbol at a console is a local exploit's dream. But in the current privelaged land of root, it is not uncommon to face challanges. Lets take a simple example: a setuid root binary, data read from a file and echoed onto the stdout. Lets make this a little easier: the binary is not stripped. Lets make this a little more easier: ASLR & Stack Cookies are disabled... for now. At the same time, it's a 64 bit system, NX is enabled [Hardware Enforced] & we don't have the code.
Enough talking, lets get our hands dirty...
A sample run:
$> ./test
usage: ./test file_name
$> echo "hello" > foo
$> ./test foo
Data Read: hello
looks good, the program takes a filename as input, reads the file and echos the file contents on stdout.
lets try to spice it up a bit, run the program with input incremented by 100 characters each time:
for the character stream generation, we shall use good ol' python.
$> python -c "print 'A'*100" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... [OUTPUT TRUNCATED]
$> python -c "print 'A'*200" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
$> python -c "print 'A'*300" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
Seems good till now, maybe the code isn't vulnerable, but that wouldn't be fun would it ;)
lets keep trying...
$> python -c "print 'A'*400" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... [OUTPUT TRUNCATED]
$> python -c "print 'A'*500" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... [OUTPUT TRUNCATED]
Maybe this is a lost cause, we might as well go and check our email...
"Patience my padawan learner"
$> python -c "print 'A'*600" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
Segmentation Fault (core dumped)
bullseye! segfault, the buffer doesn't seem to be unlimited afterall.
Looking at all the attempts, we can easily say that the buffer lies between 500 to 600 bytes.
Lets fireup GDB for a bit of code and stack analysis.
$> gdb test
GNU gdb (GDB) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
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 "x86_64-unknown-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /home/sandman/test...(no debugging symbols found)...done.
(gdb) run foo
Starting program: /home/sandman/test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
Program received signal SIGSEGV, Segmentation fault.
0x00000000004007af in echo_me ()
Seems like the code segfaults in the function echo_me(), a little bit of disassembly required (thankfully the binary is not stripped)...
(gdb) disas echo_me
Dump of assembler code for function echo_me:
0x00000000004006d4 <+0>: push %rbp
0x00000000004006d5 <+1>: mov %rsp,%rbp
0x00000000004006d8 <+4>: sub $0x240,%rsp
0x00000000004006df <+11>: mov %rdi,-0x238(%rbp)
0x00000000004006e6 <+18>: mov $0x4008ec,%edx
0x00000000004006eb <+23>: mov -0x238(%rbp),%rax
0x00000000004006f2 <+30>: mov %rdx,%rsi
0x00000000004006f5 <+33>: mov %rax,%rdi
0x00000000004006f8 <+36>: callq 0x4005b0
0x00000000004006fd <+41>: mov %rax,0x20053c(%rip) # 0x600c40
0x0000000000400704 <+48>: mov 0x200535(%rip),%rax # 0x600c40
0x000000000040070b <+55>: test %rax,%rax
0x000000000040070e <+58>: jne 0x400724
0x0000000000400710 <+60>: mov $0x4008ee,%edi
0x0000000000400715 <+65>: callq 0x400580
0x000000000040071a <+70>: mov $0xffffffff,%edi
0x000000000040071f <+75>: callq 0x4005a0
0x0000000000400724 <+80>: mov 0x200515(%rip),%rax # 0x600c40
0x000000000040072b <+87>: mov $0x2,%edx
0x0000000000400730 <+92>: mov $0x0,%esi
0x0000000000400735 <+97>: mov %rax,%rdi
0x0000000000400738 <+100>: callq 0x400590
0x000000000040073d <+105>: mov 0x2004fc(%rip),%rax # 0x600c40
0x0000000000400744 <+112>: mov %rax,%rdi
0x0000000000400747 <+115>: callq 0x400570
0x000000000040074c <+120>: mov %eax,-0x4(%rbp)
0x000000000040074f <+123>: mov 0x2004ea(%rip),%rax # 0x600c40
0x0000000000400756 <+130>: mov $0x0,%edx
0x000000000040075b <+135>: mov $0x0,%esi
0x0000000000400760 <+140>: mov %rax,%rdi
0x0000000000400763 <+143>: callq 0x400590
0x0000000000400768 <+148>: mov 0x2004d1(%rip),%rdx # 0x600c40
0x000000000040076f <+155>: mov -0x4(%rbp),%ecx
0x0000000000400772 <+158>: lea -0x230(%rbp),%rax
0x0000000000400779 <+165>: mov %ecx,%esi
0x000000000040077b <+167>: mov %rax,%rdi
0x000000000040077e <+170>: callq 0x4005d0
0x0000000000400783 <+175>: mov 0x2004b6(%rip),%rax # 0x600c40
0x000000000040078a <+182>: mov %rax,%rdi
0x000000000040078d <+185>: callq 0x4005e0
0x0000000000400792 <+190>: mov $0x4008fa,%eax
0x0000000000400797 <+195>: lea -0x230(%rbp),%rdx
0x000000000040079e <+202>: mov %rdx,%rsi
0x00000000004007a1 <+205>: mov %rax,%rdi
0x00000000004007a4 <+208>: mov $0x0,%eax
0x00000000004007a9 <+213>: callq 0x400560
0x00000000004007ae <+218>: leaveq
=> 0x00000000004007af <+219>: retq
End of assembler dump.
(gdb)
looking at the disassembly, its quite easy to get an idea of the code by a simple follow of the important @plt [procedure linkage table] calls...
function echo_me(){
fopen(file) | open the file
fseek(till EOF) --|
ftell(file_des) |--> to calculate the length of the file
fseek(till BOF) --|
fgets(the string from the file) | get the string from the file and store it in the buffer [the vulnerability lies here]
fclose(file) | close the file
printf(the string) | print the string
}
looking at all this it is evident what the programmer has done. A buffer with a static size was defined, the file size was calculated and was read into the buffer. Finally, the buffer is displayed as the output.
1. A modest amount of core knowledge is assumed.
2. A read of "Smashing The Stack for Fun and Profit" by Aleph One would be recommeded.
3. Point '2' doesn't guarante anything and for further queries refer to point '1' or ask away under comments.
To get the # symbol at a console is a local exploit's dream. But in the current privelaged land of root, it is not uncommon to face challanges. Lets take a simple example: a setuid root binary, data read from a file and echoed onto the stdout. Lets make this a little easier: the binary is not stripped. Lets make this a little more easier: ASLR & Stack Cookies are disabled... for now. At the same time, it's a 64 bit system, NX is enabled [Hardware Enforced] & we don't have the code.
Enough talking, lets get our hands dirty...
A sample run:
$> ./test
usage: ./test file_name
$> echo "hello" > foo
$> ./test foo
Data Read: hello
looks good, the program takes a filename as input, reads the file and echos the file contents on stdout.
lets try to spice it up a bit, run the program with input incremented by 100 characters each time:
for the character stream generation, we shall use good ol' python.
$> python -c "print 'A'*100" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... [OUTPUT TRUNCATED]
$> python -c "print 'A'*200" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
$> python -c "print 'A'*300" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
Seems good till now, maybe the code isn't vulnerable, but that wouldn't be fun would it ;)
lets keep trying...
$> python -c "print 'A'*400" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... [OUTPUT TRUNCATED]
$> python -c "print 'A'*500" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... [OUTPUT TRUNCATED]
Maybe this is a lost cause, we might as well go and check our email...
"Patience my padawan learner"
$> python -c "print 'A'*600" > foo && ./test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
Segmentation Fault (core dumped)
bullseye! segfault, the buffer doesn't seem to be unlimited afterall.
Looking at all the attempts, we can easily say that the buffer lies between 500 to 600 bytes.
Lets fireup GDB for a bit of code and stack analysis.
$> gdb test
GNU gdb (GDB) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
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 "x86_64-unknown-linux-gnu".
For bug reporting instructions, please see:
Reading symbols from /home/sandman/test...(no debugging symbols found)...done.
(gdb) run foo
Starting program: /home/sandman/test foo
Data Read: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
Program received signal SIGSEGV, Segmentation fault.
0x00000000004007af in echo_me ()
Seems like the code segfaults in the function echo_me(), a little bit of disassembly required (thankfully the binary is not stripped)...
(gdb) disas echo_me
Dump of assembler code for function echo_me:
0x00000000004006d4 <+0>: push %rbp
0x00000000004006d5 <+1>: mov %rsp,%rbp
0x00000000004006d8 <+4>: sub $0x240,%rsp
0x00000000004006df <+11>: mov %rdi,-0x238(%rbp)
0x00000000004006e6 <+18>: mov $0x4008ec,%edx
0x00000000004006eb <+23>: mov -0x238(%rbp),%rax
0x00000000004006f2 <+30>: mov %rdx,%rsi
0x00000000004006f5 <+33>: mov %rax,%rdi
0x00000000004006f8 <+36>: callq 0x4005b0
0x00000000004006fd <+41>: mov %rax,0x20053c(%rip) # 0x600c40
0x0000000000400704 <+48>: mov 0x200535(%rip),%rax # 0x600c40
0x000000000040070b <+55>: test %rax,%rax
0x000000000040070e <+58>: jne 0x400724
0x0000000000400710 <+60>: mov $0x4008ee,%edi
0x0000000000400715 <+65>: callq 0x400580
0x000000000040071a <+70>: mov $0xffffffff,%edi
0x000000000040071f <+75>: callq 0x4005a0
0x0000000000400724 <+80>: mov 0x200515(%rip),%rax # 0x600c40
0x000000000040072b <+87>: mov $0x2,%edx
0x0000000000400730 <+92>: mov $0x0,%esi
0x0000000000400735 <+97>: mov %rax,%rdi
0x0000000000400738 <+100>: callq 0x400590
0x000000000040073d <+105>: mov 0x2004fc(%rip),%rax # 0x600c40
0x0000000000400744 <+112>: mov %rax,%rdi
0x0000000000400747 <+115>: callq 0x400570
0x000000000040074c <+120>: mov %eax,-0x4(%rbp)
0x000000000040074f <+123>: mov 0x2004ea(%rip),%rax # 0x600c40
0x0000000000400756 <+130>: mov $0x0,%edx
0x000000000040075b <+135>: mov $0x0,%esi
0x0000000000400760 <+140>: mov %rax,%rdi
0x0000000000400763 <+143>: callq 0x400590
0x0000000000400768 <+148>: mov 0x2004d1(%rip),%rdx # 0x600c40
0x000000000040076f <+155>: mov -0x4(%rbp),%ecx
0x0000000000400772 <+158>: lea -0x230(%rbp),%rax
0x0000000000400779 <+165>: mov %ecx,%esi
0x000000000040077b <+167>: mov %rax,%rdi
0x000000000040077e <+170>: callq 0x4005d0
0x0000000000400783 <+175>: mov 0x2004b6(%rip),%rax # 0x600c40
0x000000000040078a <+182>: mov %rax,%rdi
0x000000000040078d <+185>: callq 0x4005e0
0x0000000000400792 <+190>: mov $0x4008fa,%eax
0x0000000000400797 <+195>: lea -0x230(%rbp),%rdx
0x000000000040079e <+202>: mov %rdx,%rsi
0x00000000004007a1 <+205>: mov %rax,%rdi
0x00000000004007a4 <+208>: mov $0x0,%eax
0x00000000004007a9 <+213>: callq 0x400560
0x00000000004007ae <+218>: leaveq
=> 0x00000000004007af <+219>: retq
End of assembler dump.
(gdb)
looking at the disassembly, its quite easy to get an idea of the code by a simple follow of the important @plt [procedure linkage table] calls...
function echo_me(){
fopen(file) | open the file
fseek(till EOF) --|
ftell(file_des) |--> to calculate the length of the file
fseek(till BOF) --|
fgets(the string from the file) | get the string from the file and store it in the buffer [the vulnerability lies here]
fclose(file) | close the file
printf(the string) | print the string
}
looking at all this it is evident what the programmer has done. A buffer with a static size was defined, the file size was calculated and was read into the buffer. Finally, the buffer is displayed as the output.
No comments:
Post a Comment