first commit

This commit is contained in:
criyle 2019-03-26 20:55:28 -04:00
commit f6ed5aaa41
5 changed files with 325 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# OS
.DS_Store
# Test Env
test1/
env.sh

3
README.md Normal file
View File

@ -0,0 +1,3 @@
## go-judger
Trying to used libseccomp with ptrace in GO.

163
runner/main.go Normal file
View File

@ -0,0 +1,163 @@
package main
import (
"log"
"os"
"runtime"
"time"
libseccomp "github.com/seccomp/libseccomp-golang"
unix "golang.org/x/sys/unix"
)
var (
defaultAllows = []string{
"read",
"write",
"readv",
"writev",
"open",
"unlink",
"close",
"readlink",
"openat",
"unlinkat",
"readlinkat",
"stat",
"fstat",
"lstat",
"lseek",
"access",
"dup",
"dup2",
"dup3",
"ioctl",
"fcntl",
"mmap",
"mprotect",
"munmap",
"brk",
"mremap",
"msync",
"mincore",
"madvise",
"rt_sigaction",
"rt_sigprocmask",
"rt_sigreturn",
"rt_sigpending",
"sigaltstack",
"getcwd",
"exit",
"exit_group",
"arch_prctl",
"gettimeofday",
"getrlimit",
"getrusage",
"times",
"time",
"clock_gettime",
"restart_syscall",
}
defaultTraces = []string{
"execve",
}
)
func buildFilter(allows, traces []string) (*libseccomp.ScmpFilter, error) {
// make filter
//filter, err := libseccomp.NewFilter(libseccomp.ActErrno.SetReturnCode(int16(syscall.EPERM)))
filter, err := libseccomp.NewFilter(libseccomp.ActTrace.SetReturnCode(100))
if err != nil {
return nil, err
}
for _, s := range allows {
//log.Println("[+] allow syscall: ", s)
syscallId, err := libseccomp.GetSyscallFromName(s)
if err != nil {
return nil, err
}
filter.AddRule(syscallId, libseccomp.ActAllow)
}
for _, s := range traces {
//log.Println("[+] trace syscall: ", s)
syscallId, err := libseccomp.GetSyscallFromName(s)
if err != nil {
return nil, err
}
//filter.AddRule(syscallId, libseccomp.ActAllow)
filter.AddRule(syscallId, libseccomp.ActTrace.SetReturnCode(10))
}
return filter, nil
}
func main() {
// Ptrace require running at the same thread
runtime.LockOSThread()
defer runtime.UnlockOSThread()
filter, err := buildFilter(defaultAllows, defaultTraces)
if err != nil {
log.Fatal("Failed to create filter: ", err)
}
// run in restricted mode
pid, err := ForkAndLoadSeccomp(os.Args[1:], filter)
if err != nil {
log.Fatal("Failed to fork: ", err)
}
log.Println("After fork")
// Set real time limit
timer := time.AfterFunc(time.Duration(1e9), func() {
log.Println("Before kill")
unix.Kill(pid, unix.SIGKILL)
log.Println("After kill")
})
defer timer.Stop()
// Set trace seccomp
unix.PtraceSetOptions(pid, unix.PTRACE_O_TRACESECCOMP)
log.Println("Strat trace pid: ", pid)
// trace unixs
for {
var wstatus unix.WaitStatus
var rusage unix.Rusage
_, err := unix.Wait4(pid, &wstatus, unix.WALL, &rusage)
if err != nil {
log.Fatalln("Wait4 fatal: ", err)
}
if wstatus.Exited() {
log.Println("Exited", wstatus.ExitStatus())
break
}
if wstatus.Signaled() {
log.Println("Signal", wstatus.Signal())
}
if wstatus.Stopped() {
log.Println("Stopped")
if wstatus.TrapCause() == unix.PTRACE_EVENT_SECCOMP {
log.Println("Seccomp Traced")
msg, err := unix.PtraceGetEventMsg(pid)
if err != nil {
log.Fatalln(err)
}
log.Println("Ptrace Event: ", msg)
} else {
log.Println("Stop Cause: ", wstatus.TrapCause())
}
}
log.Println("Ptrace continue")
unix.PtraceCont(pid, 0)
}
}

118
runner/run_child.go Normal file
View File

@ -0,0 +1,118 @@
package main
import (
"syscall"
"unsafe" // required for go:linkname.
libseccomp "github.com/seccomp/libseccomp-golang"
"golang.org/x/sys/unix"
)
//go:linkname beforeFork syscall.runtime_BeforeFork
func beforeFork()
//go:linkname afterFork syscall.runtime_AfterFork
func afterFork()
//go:linkname afterForkInChild syscall.runtime_AfterForkInChild
func afterForkInChild()
// ForkAndLoadSeccomp will fork, load seccomp and execv and being traced by ptrace
// Reference to src/syscall/exec_linux.go
// The runtime OS thread must be locked before calling this function
//go:noinline
//go:norace
func ForkAndLoadSeccomp(args []string, filter *libseccomp.ScmpFilter) (int, error) {
var (
err1 syscall.Errno
)
// make exec args
argv0, err := syscall.BytePtrFromString(args[0])
if err != nil {
return 0, err
}
argv, err := syscall.SlicePtrFromStrings(args)
if err != nil {
return 0, err
}
envv, err := syscall.SlicePtrFromStrings([]string{""})
if err != nil {
return 0, err
}
// make bpf using libseccomp
bpf, err := FilterToBPF(filter)
if err != nil {
return 0, err
}
// About to call fork.
// No more allocation or calls of non-assembly functions.
beforeFork()
pid, _, err1 := syscall.RawSyscall6(syscall.SYS_CLONE, uintptr(syscall.SIGCHLD), 0, 0, 0, 0, 0)
if err1 != 0 || pid != 0 {
// restore all signals
afterFork()
if err1 != 0 {
return int(pid), syscall.Errno(err1)
}
return int(pid), nil
}
// In child process
afterForkInChild()
// Notice: cannot call any functions beyond this point
// Enable ptrace
_, _, err1 = syscall.RawSyscall(syscall.SYS_PTRACE, uintptr(syscall.PTRACE_TRACEME), 0, 0)
if err1 != 0 {
goto childerror
}
// Check if support
// SECCOMP_SET_MODE_STRICT = 0, args = 1 for invalid operation
_, _, err1 = syscall.Syscall(unix.SYS_SECCOMP, 0, 1, 0)
if err1 != syscall.EINVAL {
goto childerror
}
// Load the filter manually
// No new priv
_, _, err1 = syscall.Syscall6(syscall.SYS_PRCTL, unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0, 0)
if err1 != 0 {
goto childerror
}
// Get pid of child
pid, _, err1 = syscall.Syscall(syscall.SYS_GETPID, 0, 0, 0)
if err1 != 0 {
goto childerror
}
// Stop to wait for tracer
_, _, err1 = syscall.Syscall(syscall.SYS_KILL, pid, uintptr(syscall.SIGSTOP), 0)
if err1 != 0 {
goto childerror
}
// set seccomp
//_, _, err1 = syscall.Syscall6(syscall.SYS_PRCTL, unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, uintptr(unsafe.Pointer(&bpf[0])), 0, 0, 0)
// SECCOMP_SET_MODE_FILTER = 1
// SECCOMP_FILTER_FLAG_TSYNC = 1
_, _, err1 = syscall.Syscall(unix.SYS_SECCOMP, 1, 1, uintptr(unsafe.Pointer(bpf)))
if err1 != 0 {
goto childerror
}
// time to exec
_, _, err1 = syscall.RawSyscall(syscall.SYS_EXECVE,
uintptr(unsafe.Pointer(argv0)),
uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&envv[0])))
childerror:
syscall.RawSyscall(syscall.SYS_EXIT, uintptr(err1), 0, 0)
// cannot reach this point
panic("cannot reach")
}

35
runner/util.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"io/ioutil"
"os"
"syscall"
"unsafe"
libseccomp "github.com/seccomp/libseccomp-golang"
)
// FilterToBPF convert libseccomp filter to kernel readable BPF style
func FilterToBPF(filter *libseccomp.ScmpFilter) (*syscall.SockFprog, error) {
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
// export to pipe
go func() {
filter.ExportBPF(w)
filter.Release()
w.Close()
}()
// get BPF binary
bin, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
// directly convert pointer
return &syscall.SockFprog{
Len: uint16(len(bin) / 8),
Filter: (*syscall.SockFilter)(unsafe.Pointer(&bin[0])),
}, nil
}