printf oriented programming 工具介绍

这篇文章主要针对Usenix security 2015的control flow bending这篇论文提到的工具(printbf – Brainfuck interpreter in printf)进行简单的介绍
开源链接:https://github.com/HexHive/printbf

获取源码

git clone https://github.com/HexHive/printbf

进入到./src文件夹下

make

就能生成一堆demo文件。

查看Makefile文件,就知道这个printbf解释器的用法了。

python toker.py -t pbf_pre.c -bf ../bfprogs/hello.bf > pbf_hello.c
gcc -W -Wall -Wextra -Wpedantic -O3 -U_FORTIFY_SOURCE pbf_hello.c -o pbf_hello

toker.py脚本利用pbf_pre.c的格式化字符串漏洞,实现打印hello world的功能。一般利用漏洞都是构造一个奇妙的输入来实现exploit。这里作者构造的输入比较长,为了方便使用,作者弄了一个数组prog来代替标准输入。

用notepad++对比原始漏洞程序pbf_pre.c和被利用的程序pbf_hello.c。我们可以看到pbf_hello.c就是多了这块数组的初始化赋值部分。真实情况里,我们可以将这部分视为外部输入。

在这里插入图片描述

然后用gcc编译程序,并执行,结果如下。

在这里插入图片描述

可以看到漏洞程序被printf_oriented_programming的方法改成了一个hello world程序。

最后用一个图来总结一下:

在这里插入图片描述

附:漏洞程序代码pbf_pre.c:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#define ARRSIZE (2<<24)

char buf[250000];

/* array will be used to not spill to stdout */
unsigned char* array_;
unsigned char* array;
int ptr;

int progn_[1000000];
int* progn;
int pc;

char* output_;
char* output;
int outptr;

char* input_;
char* input;
int inptr;


char* format_specifiers;

/* Arrays that hold our format strings */
char bf_LT_fmt[100];
char bf_GT_fmt[100];
char bf_PLUS_fmt[100];
char bf_MINUS_fmt[100];
char bf_DOT_fmt1[100];
char bf_DOT_fmt2[100];
char bf_GOTO_fmt[100];
char bf_COMMA_fmt1[100];
char bf_COMMA_fmt2[100];
char bf_CGOTO_fmt1[100];
char bf_CGOTO_fmt2[100];
char bf_CGOTO_fmt3[100];

char bf_ZERO_fmt[100];
char bf_FORWARD_N_fmt[100];
char bf_BACKWARD_N_fmt1[100];
char bf_BACKWARD_N_fmt2[100];
char bf_BACKWARD_N_fmt3[100];
char bf_ADD_N_fmt[100];

char bf_FETCH_fmt[100];

char* syms[] = {
  0,
  bf_LT_fmt, 
  bf_GT_fmt, 
  bf_PLUS_fmt, 
  bf_MINUS_fmt, 
  bf_DOT_fmt1, 
  bf_DOT_fmt2, 
  bf_COMMA_fmt1, 
  bf_COMMA_fmt2, 
  bf_CGOTO_fmt1, 
  bf_CGOTO_fmt2, 
  bf_CGOTO_fmt3, 
  bf_GOTO_fmt, 

  bf_ZERO_fmt, 
  bf_FORWARD_N_fmt, 
  bf_BACKWARD_N_fmt1, 
  bf_BACKWARD_N_fmt2, 
  bf_BACKWARD_N_fmt3, 
  bf_ADD_N_fmt,
  bf_FETCH_fmt
};

char** real_syms;

/**
 * setup - initialize basic variables and status for interpreter
 * Initialize interpreter, nothing that can't be done without a couple
 * of controlled memory writes and some limited alignment.
 * No cheating here, move along.
 */
void setup() {
  unsigned int i;
  inptr = 0;
  ptr = 0;
  pc = 2;

  real_syms = (char**)calloc(1,1<<17);
  real_syms = (char**)((char*)real_syms + 0x10000-(((long)real_syms)&0xFFFF));

  for (i = 0; i < sizeof syms/sizeof(*syms); i++) {
    real_syms[i] = syms[i];
  }

  array_ = malloc(ARRSIZE);
  array = (void*)((char*)array_ + 0x1000000-(((long)array_)&0xFFFFFF));
  output_ = malloc(ARRSIZE);
  output = (void*)((char*)output_ + 0x1000000-(((long)output_)&0xFFFFFF));
  input_ = malloc(ARRSIZE);
  input = (void*)((char*)input_ + 0x1000000-(((long)input_)&0xFFFFFF));

  progn = (void*)((char*)progn_ + 0x10000-(((long)progn_)&0xFFFF));

  for (i = 0; i < ARRSIZE/sizeof(*array); i++) {
    array_[i] = 0;
  }
  for (i = 0; i < sizeof progn_/sizeof(*progn); i++) {
    progn_[i] = 0;
  }
  for (i = 0; i < ARRSIZE /sizeof(*input); i++) {
    input_[i] = -1;
  }

  strcpy(bf_LT_fmt, "%1$65535d%1$.*1$d%2$hn");
  strcpy(bf_GT_fmt, "%1$.*1$d %2$hn");
  strcpy(bf_PLUS_fmt, "%3$.*3$d %4$hhn");
  strcpy(bf_MINUS_fmt, "%3$255d%3$.*3$d%4$hhn");

  strcpy(bf_DOT_fmt1, "%3$.*3$d%5$hn");
  strcpy(bf_DOT_fmt2, "%6$.*6$d %7$hn");

  strcpy(bf_COMMA_fmt1, "%13$.*13$d%4$hn");
  strcpy(bf_COMMA_fmt2, "%14$.*14$d %15$hn");

  strcpy(bf_GOTO_fmt, "%10$.*10$d%11$hn");

  strcpy(bf_CGOTO_fmt1, "%8$n%4$s%8$hhn"); 
  strcpy(bf_CGOTO_fmt2, "%8$s%12$255d%9$hhn");
  strcpy(bf_CGOTO_fmt3, "    %10$.*10$d%11$hn"); 

  strcpy(bf_ZERO_fmt, "%4$hhn"); 

  strcpy(bf_FORWARD_N_fmt, "%1$.*1$d%10$.*10$d%2$hn"); 

  strcpy(bf_BACKWARD_N_fmt1, "%1$.*1$d%10$.*10$d%2$hn");

  strcpy(bf_ADD_N_fmt, "%3$.*3$d%10$.*10$d%4$hhn"); 

  strcpy(bf_FETCH_fmt, "%1$.*1$d%1$.*1$d%1$.*1$d%1$.*1$d%1$.*1$d%1$.*1$d%1$."
      "*1$d%1$.*1$d%2$hn");
}

int cond = 0;

/**
 * Loop --execute one bf instruction
 * Main interpreter loop, with only slight cheating to get around
 * overwriting printf internal data structures.
 * First decode the next bf instruction and set corresponding ptrs
 * then execute the instruction and update the output.
 */
void loop() {
  char* last = output;
  int* rpc = &progn[pc];

#ifdef NOCHEATING  
  long copyarray = 0;
  long copyoutput = 0;
  long copyinput = 0;
  int rpc1;
#endif

  while (*rpc != 0) {
    /* fetch -- decode next instruction */
    sprintf(buf, bf_FETCH_fmt, *rpc, (short*)(&real_syms));

#ifdef NOCHEATING
    sprintf((void*)(&copyarray), "%s", (char*)(&array));
    sprintf((void*)(&copyinput), "%s", (char*)(&input));
    sprintf((void*)(&copyoutput), "%s", (char*)(&output));
    sprintf((void*)(&rpc1), "%s", (char*)(&rpc[1]));
    sprintf((char*)(&rpc1)+1, "%s", (char*)(&rpc[1])+1);

    /* execute instruction */
    sprintf(buf, *real_syms,
	    copyarray, &array, /* 1, 2 */
	    *array, array, /* 3, 4 */
	    output, /* 5 */
	    copyoutput, &output, /* 6, 7 */
	    &cond, &bf_CGOTO_fmt3[0], /* 8, 9 */
	    rpc1, &rpc, /* 10, 11 */
	    0, /* 12 */
	    *input, /* 13 */
	    copyinput, &input /* 14, 15 */
	    );
#else
    /* execute instruction */
    sprintf(buf, *real_syms,
      /* slight cheating for bitwise and */
	    ((long)array)&0xFFFF, &array, /* 1, 2 */
	    *array, array, /* 3, 4 */
	    output, /* 5 */
      /* slight cheating for bitwise and */
      ((long)output)&0xFFFF, &output, /* 6, 7 */
	    &cond, &bf_CGOTO_fmt3[0], /* 8, 9 */
	    rpc[1], &rpc, /* 10, 11 */
	    0, /* 12 */
	    *input, /* 13 */
      /* slight cheating for bitwise and */
	    ((long)input)&0xFFFF, &input /* 14, 15 */
	    );
#endif
    /* retire -- update PC */
    sprintf(buf, "12345678%.*d%hn", 
      (int)(((long)rpc)&0xFFFF), 0, (short*)&rpc);

    /* for debug: do we need to print? */
    if (output != last) {
      putchar(output[-1]);
      last = output;
    }
  }
}

int main() {
  struct timeval tval_before, tval_after, tval_result;

  setup();

  /* program */
  ###TOKER###

  gettimeofday(&tval_before, NULL);

  strcpy(input, "###INPUT###");

  /* execute the bf program */
  loop();

  gettimeofday(&tval_after, NULL);

  timersub(&tval_after, &tval_before, &tval_result);

  printf("Time elapsed: %ld.%06ld\n", 
    (long int)tval_result.tv_sec,
    (long int)tval_result.tv_usec
    );

  return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

破落之实

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值