我想禁用我的Go程序的堆栈保护。我正在尝试模拟一个易受攻击的 C 库,并希望从那里透视到 Go 代码。但是,我似乎找不到正确的标志来禁用堆栈粉碎检测。
这是我的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 检查文件还告诉我该选项仍然处于活动状态。 您能否指出一些提示,说明我做错了什么或应该使用哪些标志来禁用此功能?
非常感谢!
从 Go cgo 命令文档开始。
命令 cgo
将 cgo 与 go 命令结合使用
要使用 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
$