mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
first commit
This commit is contained in:
commit
f6ed5aaa41
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Test Env
|
||||
test1/
|
||||
env.sh
|
||||
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
## go-judger
|
||||
|
||||
Trying to used libseccomp with ptrace in GO.
|
||||
163
runner/main.go
Normal file
163
runner/main.go
Normal 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
118
runner/run_child.go
Normal 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
35
runner/util.go
Normal 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
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user