停用Go编译程序的堆栈保护

时间:2018-10-04 16:39:00

标签: c security go compilation

我想为我的Go程序禁用堆栈保护。我正在尝试模拟一个易受攻击的C库,并希望从那里转到Go代码。但是,我似乎找不到正确的标志来禁用堆栈粉碎检测。

这是我的转到代码:

package main

import "os"
import "fmt"

/*
#include "test.h"
*/
import "C"

func main() {
    if (len(os.Args) >= 2){
    argsWithoutProg := os.Args[1:]
        if (argsWithoutProg[0] == "admin") {
            secret();
        }
    } else {
        regular()
    }
}

func regular() {
    fmt.Println("Go: BORING")
    C.hackme()
}

func secret() {
    fmt.Println("Go: SECRET FUNC")
}

这是我的c库代码:

// #cgo CFLAGS: -g -O3 -fno-stack-protector
#include <stdint.h>
#include <stdio.h>

void hackme();

// this function is vulnerable and is used as an entrypoint to the go part
void hackme() {
    char buf[3];
    int r;
    r = read(0, buf, 300);
    printf("C: %d bytes read. Content: %s!\n", r, buf);
    return;
}

我使用go build -a poc.go进行编译。

如您所见,我已经在C库的开头添加了一些CFLAGS指令,但它们似乎无济于事。以前,我尝试通过我的编译命令中的-gcflags开关添加它们,但这也是徒劳的。每当我尝试用300 * A字符串攻击程序时,都会被检测到:

Go: BORING
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
C: 300 bytes read. Content: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
*** stack smashing detected ***: <unknown> terminated
SIGABRT: abort
PC=0x7fd263dcee97 m=0 sigcode=18446744073709551610

goroutine 0 [idle]:
runtime: unknown pc 0x7fd263dcee97
stack: frame={sp:0x7ffda3507600, fp:0x0} stack=[0x7ffda2d08ad0,0x7ffda3507b00)
00007ffda3507500:  00007fd200000008  00007fd200000000
00007ffda3507510:  00007ffda3507610  0000000000000003 
[...]

使用GDB检查文件还会告诉我该选项仍处于活动状态。 您能为我指出一些有关我做错了什么或应该使用哪些标志来禁用此功能的提示吗?

非常感谢!

1 个答案:

答案 0 :(得分:2)

从Go cgo命令文档开始。

  

Command cgo

     

Using cgo with the go command

     

要使用cgo编写导入伪程序包“ C”的常规Go代码。的   然后,Go代码可以引用诸如C.size_t之类的类型,诸如   C.stdout或C.putchar之类的功能。

     

如果在导入“ C”后立即添加注释,则表示   注释,称为前导,在编译C时用作标题   包装的各个部分。例如:

// #include <stdio.h>
// #include <errno.h>
import "C"
     

序言可以包含任何C代码,包括函数和变量   声明和定义。然后可以从Go中引用这些   代码,就像它们是在“ C”包中定义的一样。所有名字   可以使用在序言中声明的内容,即使它们以   小写字母。例外:序言中的静态变量可能不会   从Go代码中引用;允许使用静态函数。

     

有关示例,请参见$ GOROOT / misc / cgo / stdio和$ GOROOT / misc / cgo / gmp。看到   “ C?去?Cgo!”有关使用cgo的介绍:   https://golang.org/doc/articles/c_go_cgo.html

     

CFLAGS,CPPFLAGS,CXXFLAGS,FFLAGS和LDFLAGS可以定义为   这些注释中的伪#cgo指令可调整行为   C,C ++或Fortran编译器。在多个指令中定义的值   串联在一起。该指令可以包含构建列表   约束将其影响限制在满足以下条件之一的系统上   约束(请参阅   https://golang.org/pkg/go/build/#hdr-Build_Constraints了解详情   关于约束语法)。例如:

// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"

尤其是:

  

要使用cgo编写导入伪程序包“ C”的普通Go代码。

     

如果在导入“ C”后立即添加注释,则表示   注释,称为前导,在编译C时用作标题   包装的一部分。

     

CFLAGS可以用   这些注释中的伪#cgo指令可调整行为   C编译器。

例如:

/*
#cgo CFLAGS: -g -O3 -fno-stack-protector
#include "test.h"
*/
import "C"

输出(未检测到堆栈粉碎):

$ go build -a poc.go && ./poc
Go: BORING
AAAAAAAAAAAAAAA
C: 16 bytes read. Content: AAAAAAAAAAAAAAA
!
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0xa41414141 pc=0xa41414141]

runtime stack:
runtime.throw(0x4bb802, 0x2a)
    /home/peter/go/src/runtime/panic.go:608 +0x72
runtime.sigpanic()
    /home/peter/go/src/runtime/signal_unix.go:374 +0x2ec

goroutine 1 [syscall]:
runtime.cgocall(0x484e90, 0xc000052f38, 0x0)
    /home/peter/go/src/runtime/cgocall.go:128 +0x5b fp=0xc000052f08 sp=0xc000052ed0 pc=0x403deb
main._Cfunc_hackme()
    _cgo_gotypes.go:41 +0x41 fp=0xc000052f38 sp=0xc000052f08 pc=0x484c51
main.regular()
    /home/peter/gopath/src/poc/poc.go:25 +0x62 fp=0xc000052f88 sp=0xc000052f38 pc=0x484d52
main.main()
    /home/peter/gopath/src/poc/poc.go:19 +0x65 fp=0xc000052f98 sp=0xc000052f88 pc=0x484cd5
runtime.main()
    /home/peter/go/src/runtime/proc.go:201 +0x1ec fp=0xc000052fe0 sp=0xc000052f98 pc=0x42928c
runtime.goexit()
    /home/peter/go/src/runtime/asm_amd64.s:1340 +0x1 fp=0xc000052fe8 sp=0xc000052fe0 pc=0x450cd1
$

poc.go

package main

import "os"
import "fmt"

/*
#cgo CFLAGS: -g -O3 -fno-stack-protector
#include "test.h"
*/
import "C"

func main() {
    if (len(os.Args) >= 2){
    argsWithoutProg := os.Args[1:]
        if (argsWithoutProg[0] == "admin") {
            secret();
        }
    } else {
        regular()
    }
}

func regular() {
    fmt.Println("Go: BORING")
    C.hackme()
}

func secret() {
    fmt.Println("Go: SECRET FUNC")
}

test.h

#include <stdint.h>
#include <stdio.h>

void hackme();

// this function is vulnerable and is used as an entrypoint to the go part
void hackme() {
    char buf[3];
    int r;
    r = read(0, buf, 300);
    printf("C: %d bytes read. Content: %s!\n", r, buf);
    return;
}

没有-fno-stack-protector

/*
#cgo CFLAGS: -g -O3
#include "test.h"
*/
import "C"

输出(检测到堆栈粉碎):

$ go build -a poc.go && ./poc
Go: BORING
AAAAAAAAAAAAAAA
C: 16 bytes read. Content: AAAAAAAAAAAAAAA
!
*** stack smashing detected ***: <unknown> terminated
SIGABRT: abort
PC=0x7f1c5323ee97 m=0 sigcode=18446744073709551610
$