1
0

12 Commits

12 changed files with 215 additions and 72 deletions

12
.github/workflows/macos.yml vendored Normal file
View File

@ -0,0 +1,12 @@
name: macOS
on: [push]
jobs:
build:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Build
run: go build

23
.github/workflows/ubuntu.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Ubuntu
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Build
run: go build
- name: Install dependencies for tests
run: |
sudo apt-get update
sudo apt-get install qemu
- name: Bootstrap
run: ./tools/qemu-debian-img/bootstrap.sh
- name: Test
run: go test -parallel 1 -v ./...

View File

@ -4,6 +4,40 @@
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.2.1] 2019-12-25
### Fixed
- macOS support.
## [1.2.0] 2019-11-15
### Added
- Flag for Verbose output. Right now only qemu status messages is
implemented.
### Fixed
- Kpti settings was not affected for regular runs.
## [1.1.2] 2019-09-05
### Added
- Added policykit-1 to rootfs for Ubuntu.
### Fixed
- Avoided slow mirrors with use of mirror://mirrors.ubuntu.com for
Ubuntu 16.04 and newer.
## [1.1.1] 2019-08-31
### Fixed
- macOS support.
## [1.1.0] 2019-08-30 ## [1.1.0] 2019-08-30
### Added ### Added

View File

@ -1,5 +1,5 @@
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/aba4aad2046b4d1a9a99cf98e22c018b)](https://app.codacy.com/app/jollheef/out-of-tree?utm_source=github.com&utm_medium=referral&utm_content=jollheef/out-of-tree&utm_campaign=Badge_Grade_Dashboard) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/aba4aad2046b4d1a9a99cf98e22c018b)](https://app.codacy.com/app/jollheef/out-of-tree?utm_source=github.com&utm_medium=referral&utm_content=jollheef/out-of-tree&utm_campaign=Badge_Grade_Dashboard)
[![Build Status](https://travis-ci.com/jollheef/out-of-tree.svg?branch=master)](https://travis-ci.org/jollheef/out-of-tree) [![Build Status](https://travis-ci.com/jollheef/out-of-tree.svg?branch=master)](https://travis-ci.com/jollheef/out-of-tree)
[![Go Report Card](https://goreportcard.com/badge/code.dumpstack.io/tools/out-of-tree)](https://goreportcard.com/report/code.dumpstack.io/tools/out-of-tree) [![Go Report Card](https://goreportcard.com/badge/code.dumpstack.io/tools/out-of-tree)](https://goreportcard.com/report/code.dumpstack.io/tools/out-of-tree)
[![Documentation Status](https://readthedocs.org/projects/out-of-tree/badge/?version=latest)](https://out-of-tree.readthedocs.io/en/latest/?badge=latest) [![Documentation Status](https://readthedocs.org/projects/out-of-tree/badge/?version=latest)](https://out-of-tree.readthedocs.io/en/latest/?badge=latest)
[![Donate](https://img.shields.io/badge/donate-paypal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=R8W2UQPZ5X5JE&source=url) [![Donate](https://img.shields.io/badge/donate-paypal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=R8W2UQPZ5X5JE&source=url)

View File

@ -15,11 +15,11 @@ import (
"os/exec" "os/exec"
"os/user" "os/user"
"regexp" "regexp"
"runtime"
"strings" "strings"
"time" "time"
"github.com/naoina/toml" "github.com/naoina/toml"
"github.com/zcalusic/sysinfo"
"code.dumpstack.io/tools/out-of-tree/config" "code.dumpstack.io/tools/out-of-tree/config"
) )
@ -100,6 +100,14 @@ func dockerImagePath(sk config.KernelMask) (path string, err error) {
} }
func vsyscallAvailable() (available bool, err error) { func vsyscallAvailable() (available bool, err error) {
if runtime.GOOS != "linux" {
// Docker for non-Linux systems is not using the host
// kernel but uses kernel inside a virtual machine, so
// it builds by the Docker team with vsyscall support.
available = true
return
}
buf, err := ioutil.ReadFile("/proc/self/maps") buf, err := ioutil.ReadFile("/proc/self/maps")
if err != nil { if err != nil {
return return
@ -163,6 +171,13 @@ func generateBaseDockerImage(registry string, commands []config.DockerCommand,
switch sk.DistroType { switch sk.DistroType {
case config.Ubuntu: case config.Ubuntu:
d += "ENV DEBIAN_FRONTEND=noninteractive\n" d += "ENV DEBIAN_FRONTEND=noninteractive\n"
if sk.DistroRelease >= "16.04" {
from := "http://.*ubuntu/"
to := "mirror://mirrors.ubuntu.com/mirrors.txt"
file := "/etc/apt/sources.list"
s := fmt.Sprintf("sed -i 's;%s;%s;' %s", from, to, file)
d += "RUN " + s + "\n"
}
d += "RUN apt-get update\n" d += "RUN apt-get update\n"
d += "RUN apt-get install -y build-essential libelf-dev\n" d += "RUN apt-get install -y build-essential libelf-dev\n"
d += "RUN apt-get install -y wget git\n" d += "RUN apt-get install -y wget git\n"
@ -419,66 +434,6 @@ func listDockerImages() (diis []dockerImageInfo, err error) {
return return
} }
func genHostKernels(download bool) (kcfg config.KernelConfig, err error) {
si := sysinfo.SysInfo{}
si.GetSysInfo()
distroType, err := config.NewDistroType(si.OS.Vendor)
if err != nil {
return
}
cmd := exec.Command("ls", "/lib/modules")
rawOutput, err := cmd.CombinedOutput()
if err != nil {
log.Println(string(rawOutput), err)
return
}
kernelsBase := "/boot/"
files, err := ioutil.ReadDir(kernelsBase)
if err != nil {
return
}
// only for compatibility, docker is not really used
dii := dockerImageInfo{
ContainerName: config.KernelMask{
DistroType: distroType,
DistroRelease: si.OS.Version,
}.DockerName(),
}
rootfs, err := genRootfsImage(dii, download)
if err != nil {
return
}
for _, k := range strings.Fields(string(rawOutput)) {
ki := config.KernelInfo{
DistroType: distroType,
DistroRelease: si.OS.Version,
KernelRelease: k,
KernelSource: "/lib/modules/" + k + "/build",
KernelPath: kernelsBase + genKernelPath(files, k),
InitrdPath: kernelsBase + genInitrdPath(files, k),
RootFS: rootfs,
}
vmlinux := "/usr/lib/debug/boot/vmlinux-" + k
log.Println("vmlinux", vmlinux)
if exists(vmlinux) {
ki.VmlinuxPath = vmlinux
}
kcfg.Kernels = append(kcfg.Kernels, ki)
}
return
}
func updateKernelsCfg(host, download bool) (err error) { func updateKernelsCfg(host, download bool) (err error) {
newkcfg := config.KernelConfig{} newkcfg := config.KernelConfig{}

77
kernel_linux.go Normal file
View File

@ -0,0 +1,77 @@
// Copyright 2018 Mikhail Klementev. All rights reserved.
// Use of this source code is governed by a AGPLv3 license
// (or later) that can be found in the LICENSE file.
// +build linux
package main
import (
"io/ioutil"
"log"
"os/exec"
"strings"
"code.dumpstack.io/tools/out-of-tree/config"
"github.com/zcalusic/sysinfo"
)
func genHostKernels(download bool) (kcfg config.KernelConfig, err error) {
si := sysinfo.SysInfo{}
si.GetSysInfo()
distroType, err := config.NewDistroType(si.OS.Vendor)
if err != nil {
return
}
cmd := exec.Command("ls", "/lib/modules")
rawOutput, err := cmd.CombinedOutput()
if err != nil {
log.Println(string(rawOutput), err)
return
}
kernelsBase := "/boot/"
files, err := ioutil.ReadDir(kernelsBase)
if err != nil {
return
}
// only for compatibility, docker is not really used
dii := dockerImageInfo{
ContainerName: config.KernelMask{
DistroType: distroType,
DistroRelease: si.OS.Version,
}.DockerName(),
}
rootfs, err := genRootfsImage(dii, download)
if err != nil {
return
}
for _, k := range strings.Fields(string(rawOutput)) {
ki := config.KernelInfo{
DistroType: distroType,
DistroRelease: si.OS.Version,
KernelRelease: k,
KernelSource: "/lib/modules/" + k + "/build",
KernelPath: kernelsBase + genKernelPath(files, k),
InitrdPath: kernelsBase + genInitrdPath(files, k),
RootFS: rootfs,
}
vmlinux := "/usr/lib/debug/boot/vmlinux-" + k
log.Println("vmlinux", vmlinux)
if exists(vmlinux) {
ki.VmlinuxPath = vmlinux
}
kcfg.Kernels = append(kcfg.Kernels, ki)
}
return
}

18
kernel_macos.go Normal file
View File

@ -0,0 +1,18 @@
// Copyright 2018 Mikhail Klementev. All rights reserved.
// Use of this source code is governed by a AGPLv3 license
// (or later) that can be found in the LICENSE file.
// +build darwin
package main
import (
"errors"
"code.dumpstack.io/tools/out-of-tree/config"
)
func genHostKernels(download bool) (kcfg config.KernelConfig, err error) {
err = errors.New("generate host kernels for macOS is not supported")
return
}

12
main.go
View File

@ -84,7 +84,7 @@ func main() {
) )
app.Author("Mikhail Klementev <root@dumpstack.io>") app.Author("Mikhail Klementev <root@dumpstack.io>")
app.Version("1.1.0") app.Version("1.2.1")
pathFlag := app.Flag("path", "Path to work directory") pathFlag := app.Flag("path", "Path to work directory")
path := pathFlag.Default(".").ExistingDir() path := pathFlag.Default(".").ExistingDir()
@ -155,6 +155,9 @@ func main() {
pewTagFlag := pewCommand.Flag("tag", "Log tagging") pewTagFlag := pewCommand.Flag("tag", "Log tagging")
pewTag := pewTagFlag.String() pewTag := pewTagFlag.String()
pewVerboseFlag := pewCommand.Flag("verbose", "Show more information")
pewVerbose := pewVerboseFlag.Bool()
kernelCommand := app.Command("kernel", "Manipulate kernels") kernelCommand := app.Command("kernel", "Manipulate kernels")
kernelNoDownload := kernelCommand.Flag("no-download", kernelNoDownload := kernelCommand.Flag("no-download",
"Do not download qemu image while kernel generation").Bool() "Do not download qemu image while kernel generation").Bool()
@ -264,6 +267,10 @@ func main() {
log.Fatalln("Only one of disable/enable can be used at once") log.Fatalln("Only one of disable/enable can be used at once")
} }
if *yekpti && *nokpti {
log.Fatalln("Only one of disable/enable can be used at once")
}
kcfg, err := config.ReadKernelConfig(*kcfgPath) kcfg, err := config.ReadKernelConfig(*kcfgPath)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
@ -299,7 +306,8 @@ func main() {
case pewCommand.FullCommand(): case pewCommand.FullCommand():
err = pewHandler(kcfg, *path, *pewKernel, *pewBinary, err = pewHandler(kcfg, *path, *pewKernel, *pewBinary,
*pewTest, *pewGuess, stop, *qemuTimeout, *dockerTimeout, *pewTest, *pewGuess, stop, *qemuTimeout, *dockerTimeout,
*pewMax, *pewRuns, *pewDist, *pewTag, *pewThreads, db) *pewMax, *pewRuns, *pewDist, *pewTag, *pewThreads,
db, *pewVerbose)
case kernelListCommand.FullCommand(): case kernelListCommand.FullCommand():
err = kernelListHandler(kcfg) err = kernelListHandler(kcfg)
case kernelAutogenCommand.FullCommand(): case kernelAutogenCommand.FullCommand():

View File

@ -51,7 +51,8 @@ func packHandler(db *sql.DB, path, registry string, stop time.Time,
pewHandler(kcfg, workPath, "", "", "", false, pewHandler(kcfg, workPath, "", "", "", false,
stop, dockerTimeout, qemuTimeout, stop, dockerTimeout, qemuTimeout,
kernelRuns, exploitRuns, pathDevNull, tag, threads, db) kernelRuns, exploitRuns, pathDevNull,
tag, threads, db, false)
} }
return return

23
pew.go
View File

@ -303,7 +303,7 @@ func copyTest(q *qemu.System, testPath string, ka config.Artifact) (
func whatever(swg *sizedwaitgroup.SizedWaitGroup, ka config.Artifact, func whatever(swg *sizedwaitgroup.SizedWaitGroup, ka config.Artifact,
ki config.KernelInfo, binaryPath, testPath string, ki config.KernelInfo, binaryPath, testPath string,
qemuTimeout, dockerTimeout time.Duration, dist, tag string, qemuTimeout, dockerTimeout time.Duration, dist, tag string,
db *sql.DB) { db *sql.DB, verbose bool) {
defer swg.Done() defer swg.Done()
@ -328,6 +328,7 @@ func whatever(swg *sizedwaitgroup.SizedWaitGroup, ka config.Artifact,
q.SetKASLR(!ka.Mitigations.DisableKaslr) q.SetKASLR(!ka.Mitigations.DisableKaslr)
q.SetSMEP(!ka.Mitigations.DisableSmep) q.SetSMEP(!ka.Mitigations.DisableSmep)
q.SetSMAP(!ka.Mitigations.DisableSmap) q.SetSMAP(!ka.Mitigations.DisableSmap)
q.SetKPTI(!ka.Mitigations.DisableKpti)
err = q.Start() err = q.Start()
if err != nil { if err != nil {
@ -336,6 +337,18 @@ func whatever(swg *sizedwaitgroup.SizedWaitGroup, ka config.Artifact,
} }
defer q.Stop() defer q.Stop()
if verbose {
go func() {
for !q.Died {
time.Sleep(time.Minute)
log.Println(ka.Name, ki.DistroType,
ki.DistroRelease, ki.KernelRelease,
"still alive")
}
}()
}
usr, err := user.Current() usr, err := user.Current()
if err != nil { if err != nil {
return return
@ -394,7 +407,7 @@ func performCI(ka config.Artifact, kcfg config.KernelConfig, binaryPath,
testPath string, stop time.Time, testPath string, stop time.Time,
qemuTimeout, dockerTimeout time.Duration, qemuTimeout, dockerTimeout time.Duration,
max, runs int64, dist, tag string, threads int, max, runs int64, dist, tag string, threads int,
db *sql.DB) (err error) { db *sql.DB, verbose bool) (err error) {
found := false found := false
@ -420,7 +433,7 @@ func performCI(ka config.Artifact, kcfg config.KernelConfig, binaryPath,
swg.Add() swg.Add()
go whatever(&swg, ka, kernel, binaryPath, go whatever(&swg, ka, kernel, binaryPath,
testPath, qemuTimeout, dockerTimeout, testPath, qemuTimeout, dockerTimeout,
dist, tag, db) dist, tag, db, verbose)
} }
} }
} }
@ -477,7 +490,7 @@ func pewHandler(kcfg config.KernelConfig,
workPath, ovrrdKrnl, binary, test string, guess bool, workPath, ovrrdKrnl, binary, test string, guess bool,
stop time.Time, qemuTimeout, dockerTimeout time.Duration, stop time.Time, qemuTimeout, dockerTimeout time.Duration,
max, runs int64, dist, tag string, threads int, max, runs int64, dist, tag string, threads int,
db *sql.DB) (err error) { db *sql.DB, verbose bool) (err error) {
ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml") ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml")
if err != nil { if err != nil {
@ -507,7 +520,7 @@ func pewHandler(kcfg config.KernelConfig,
err = performCI(ka, kcfg, binary, test, err = performCI(ka, kcfg, binary, test,
stop, qemuTimeout, dockerTimeout, stop, qemuTimeout, dockerTimeout,
max, runs, dist, tag, threads, db) max, runs, dist, tag, threads, db, verbose)
if err != nil { if err != nil {
return return
} }

View File

@ -25,7 +25,8 @@ ENV RELEASE=trusty
RUN mkdir $IMAGEDIR RUN mkdir $IMAGEDIR
# Must be executed with --privileged because of /dev/loop # Must be executed with --privileged because of /dev/loop
CMD debootstrap --include=openssh-server $RELEASE $TMPDIR $REPOSITORY && \ CMD debootstrap --include=openssh-server,policykit-1 \
$RELEASE $TMPDIR $REPOSITORY && \
/shared/setup.sh $TMPDIR && \ /shared/setup.sh $TMPDIR && \
qemu-img create $IMAGE 2G && \ qemu-img create $IMAGE 2G && \
mkfs.ext4 -F $IMAGE && \ mkfs.ext4 -F $IMAGE && \

View File

@ -26,7 +26,8 @@ ENV RELEASE=bionic
RUN mkdir $IMAGEDIR RUN mkdir $IMAGEDIR
# Must be executed with --privileged because of /dev/loop # Must be executed with --privileged because of /dev/loop
CMD debootstrap --include=openssh-server $RELEASE $TMPDIR $REPOSITORY && \ CMD debootstrap --include=openssh-server,policykit-1 \
$RELEASE $TMPDIR $REPOSITORY && \
/shared/setup.sh $TMPDIR && \ /shared/setup.sh $TMPDIR && \
qemu-img create $IMAGE 2G && \ qemu-img create $IMAGE 2G && \
mkfs.ext4 -F $IMAGE && \ mkfs.ext4 -F $IMAGE && \