seccomp-tools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: afb07d55f6cf7b437b6829709c0897af34ae112e
4
+ data.tar.gz: 72b290bd53662794f8b588d59912733096d7d79d
5
+ SHA512:
6
+ metadata.gz: 0d38df515ef8dc6474b3b407be4c3e0959cd2e28227e863c3826d8b437154c65ca7e5b385d1db4ba6ae1010f1058c25b089d2918bbe6b6b805a9bbdbe438ad07
7
+ data.tar.gz: 54730dafe8e3a77743f0d2eaa8cb947767d00b9aa3b5f2cd4a8d77386eb65decc664f74d84998d7630c7216a985cf8d8e7ca7345a63eae99aa3d9ae449d495db
@@ -0,0 +1,151 @@
1
+ [![Build Status](https://travis-ci.org/david942j/seccomp-tools.svg?branch=master)](https://travis-ci.org/david942j/seccomp-tools)
2
+ [![Code Climate](https://codeclimate.com/github/david942j/seccomp-tools/badges/gpa.svg)](https://codeclimate.com/github/david942j/seccomp-tools)
3
+ [![Issue Count](https://codeclimate.com/github/david942j/seccomp-tools/badges/issue_count.svg)](https://codeclimate.com/github/david942j/seccomp-tools)
4
+ [![Test Coverage](https://codeclimate.com/github/david942j/seccomp-tools/badges/coverage.svg)](https://codeclimate.com/github/david942j/seccomp-tools/coverage)
5
+ [![Inline docs](https://inch-ci.org/github/david942j/seccomp-tools.svg?branch=master)](https://inch-ci.org/github/david942j/seccomp-tools)
6
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)
7
+
8
+ # Seccomp Tools
9
+ Provides powerful tools for seccomp analysis.
10
+
11
+ This project is targeted to (but not limited to) analyze seccomp sandbox in CTF pwn challenges.
12
+ Some features might be CTF-specific, but still useful for analysis of seccomp in real-case.
13
+
14
+ ## Features
15
+ * Dump - Automatically dump seccomp-bpf from binary.
16
+ * Disasm - Convert bpf to human readable format.
17
+ - Simple decompile.
18
+ - Show syscall names.
19
+ - (TODO) Simplify disassemble result.
20
+ * (TODO) Solve constraints for executing syscalls (e.g. `execve/open/read/write`).
21
+ * Support multi-architectures.
22
+
23
+ ## Installation
24
+
25
+ Will be available on RubyGems.org!
26
+ (TODO)
27
+
28
+ ## Command Line Interface
29
+
30
+ ### seccomp-tools
31
+
32
+ ```bash
33
+ $ seccomp-tools --help
34
+ # Usage: seccomp-tools [--version] [--help] <command> [<options>]
35
+ #
36
+ # List of commands:
37
+ #
38
+ # dump Automatically dump seccomp bpf from execution file.
39
+ # disasm Disassembly seccomp bpf.
40
+ #
41
+ # See 'seccomp-tools --help <command>' to read about a specific subcommand.
42
+
43
+ $ seccomp-tools --help dump
44
+ # dump - Automatically dump seccomp bpf from execution file.
45
+ #
46
+ # Usage: seccomp-tools dump [exec] [options]
47
+ # -c, --sh-exec <command> Executes the given command (via sh).
48
+ # Use this option if want to pass arguments or do pipe things to the execution file.
49
+ # -f, --format FORMAT Output format. FORMAT can only be one of <disasm|raw|inspect>.
50
+ # Default: disasm
51
+ # -l, --limit LIMIT Limit the number of calling "prctl(PR_SET_SECCOMP)".
52
+ # The target process will be killed whenever its calling times reaches LIMIT.
53
+ # Default: 1
54
+ # -o, --output FILE Output result into FILE instead of stdout.
55
+ # If multiple seccomp syscalls have been invoked (see --limit),
56
+ # results will be written to FILE, FILE_1, FILE_2.. etc.
57
+ # For example, "--output out.bpf" and the output files are out.bpf, out_1.bpf, ...
58
+
59
+ ```
60
+
61
+ ### dump
62
+
63
+ Dump the seccomp bpf from an execution file.
64
+ This work is done by the `ptrace` syscall.
65
+
66
+ NOTICE: beware of the execution file will be executed.
67
+ ```bash
68
+ $ file spec/binary/twctf-2016-diary
69
+ # spec/binary/twctf-2016-diary: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=3648e29153ac0259a0b7c3e25537a5334f50107f, not stripped
70
+
71
+ $ seccomp-tools dump spec/binary/twctf-2016-diary
72
+ # line CODE JT JF K
73
+ # =================================
74
+ # 0000: 0x20 0x00 0x00 0x00000000 A = sys_number
75
+ # 0001: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0003
76
+ # 0002: 0x06 0x00 0x00 0x00000000 return KILL
77
+ # 0003: 0x15 0x00 0x01 0x00000101 if (A != openat) goto 0005
78
+ # 0004: 0x06 0x00 0x00 0x00000000 return KILL
79
+ # 0005: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0007
80
+ # 0006: 0x06 0x00 0x00 0x00000000 return KILL
81
+ # 0007: 0x15 0x00 0x01 0x00000038 if (A != clone) goto 0009
82
+ # 0008: 0x06 0x00 0x00 0x00000000 return KILL
83
+ # 0009: 0x15 0x00 0x01 0x00000039 if (A != fork) goto 0011
84
+ # 0010: 0x06 0x00 0x00 0x00000000 return KILL
85
+ # 0011: 0x15 0x00 0x01 0x0000003a if (A != vfork) goto 0013
86
+ # 0012: 0x06 0x00 0x00 0x00000000 return KILL
87
+ # 0013: 0x15 0x00 0x01 0x00000055 if (A != creat) goto 0015
88
+ # 0014: 0x06 0x00 0x00 0x00000000 return KILL
89
+ # 0015: 0x15 0x00 0x01 0x00000142 if (A != execveat) goto 0017
90
+ # 0016: 0x06 0x00 0x00 0x00000000 return KILL
91
+ # 0017: 0x06 0x00 0x00 0x7fff0000 return ALLOW
92
+
93
+ $ seccomp-tools dump spec/binary/twctf-2016-diary -f inspect
94
+ # "\x20\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x01\x02\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x01\x01\x01\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x01\x3B\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x01\x38\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x01\x39\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x01\x3A\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x01\x55\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x01\x42\x01\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\xFF\x7F"
95
+
96
+ $ seccomp-tools dump spec/binary/twctf-2016-diary -f raw | xxd
97
+ # 00000000: 2000 0000 0000 0000 1500 0001 0200 0000 ...............
98
+ # 00000010: 0600 0000 0000 0000 1500 0001 0101 0000 ................
99
+ # 00000020: 0600 0000 0000 0000 1500 0001 3b00 0000 ............;...
100
+ # 00000030: 0600 0000 0000 0000 1500 0001 3800 0000 ............8...
101
+ # 00000040: 0600 0000 0000 0000 1500 0001 3900 0000 ............9...
102
+ # 00000050: 0600 0000 0000 0000 1500 0001 3a00 0000 ............:...
103
+ # 00000060: 0600 0000 0000 0000 1500 0001 5500 0000 ............U...
104
+ # 00000070: 0600 0000 0000 0000 1500 0001 4201 0000 ............B...
105
+ # 00000080: 0600 0000 0000 0000 0600 0000 0000 ff7f ................
106
+
107
+ ```
108
+
109
+ ### disasm
110
+
111
+ Disassemble the seccomp bpf.
112
+ ```bash
113
+ $ xxd spec/data/twctf-2016-diary.bpf | head -n 3
114
+ # 00000000: 2000 0000 0000 0000 1500 0001 0200 0000 ...............
115
+ # 00000010: 0600 0000 0000 0000 1500 0001 0101 0000 ................
116
+ # 00000020: 0600 0000 0000 0000 1500 0001 3b00 0000 ............;...
117
+
118
+ $ seccomp-tools disasm spec/data/twctf-2016-diary.bpf
119
+ # line CODE JT JF K
120
+ # =================================
121
+ # 0000: 0x20 0x00 0x00 0x00000000 A = sys_number
122
+ # 0001: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0003
123
+ # 0002: 0x06 0x00 0x00 0x00000000 return KILL
124
+ # 0003: 0x15 0x00 0x01 0x00000101 if (A != openat) goto 0005
125
+ # 0004: 0x06 0x00 0x00 0x00000000 return KILL
126
+ # 0005: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0007
127
+ # 0006: 0x06 0x00 0x00 0x00000000 return KILL
128
+ # 0007: 0x15 0x00 0x01 0x00000038 if (A != clone) goto 0009
129
+ # 0008: 0x06 0x00 0x00 0x00000000 return KILL
130
+ # 0009: 0x15 0x00 0x01 0x00000039 if (A != fork) goto 0011
131
+ # 0010: 0x06 0x00 0x00 0x00000000 return KILL
132
+ # 0011: 0x15 0x00 0x01 0x0000003a if (A != vfork) goto 0013
133
+ # 0012: 0x06 0x00 0x00 0x00000000 return KILL
134
+ # 0013: 0x15 0x00 0x01 0x00000055 if (A != creat) goto 0015
135
+ # 0014: 0x06 0x00 0x00 0x00000000 return KILL
136
+ # 0015: 0x15 0x00 0x01 0x00000142 if (A != execveat) goto 0017
137
+ # 0016: 0x06 0x00 0x00 0x00000000 return KILL
138
+ # 0017: 0x06 0x00 0x00 0x7fff0000 return ALLOW
139
+
140
+ ```
141
+
142
+ ## Screenshots
143
+
144
+ ### Dump
145
+ ![dump](https://github.com/david942j/seccomp-tools/blob/master/examples/dump-diary.png?raw=true)
146
+
147
+
148
+ ## I Need You
149
+ Any suggestion or feature request is welcome!
150
+ Feel free to file an issue or send a pull request.
151
+ And, if you like this work, I'll be happy to be [stared](https://github.com/david942j/seccomp-tools/stargazers) :grimacing:
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'seccomp-tools/cli/cli'
4
+
5
+ SeccompTools::CLI.work(ARGV)
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ extension_name = 'seccomp-tools/ptrace'
4
+
5
+ create_makefile(extension_name)
@@ -0,0 +1,76 @@
1
+ #include <sys/ptrace.h>
2
+ #include <sys/signal.h>
3
+
4
+ #include "ruby.h"
5
+
6
+ static VALUE
7
+ ptrace_geteventmsg(VALUE _mod, VALUE pid) {
8
+ unsigned long val;
9
+ ptrace(PTRACE_GETEVENTMSG, NUM2LONG(pid), NULL, &val);
10
+ return ULONG2NUM(val);
11
+ }
12
+
13
+ static VALUE
14
+ ptrace_peekdata(VALUE _mod, VALUE pid, VALUE addr, VALUE _data) {
15
+ long val = ptrace(PTRACE_PEEKDATA, NUM2LONG(pid), NUM2LONG(addr), NULL);
16
+ return LONG2NUM(val);
17
+ }
18
+
19
+ static VALUE
20
+ ptrace_peekuser(VALUE _mod, VALUE pid, VALUE off, VALUE _data) {
21
+ long val = ptrace(PTRACE_PEEKUSER, NUM2LONG(pid), NUM2LONG(off), NULL);
22
+ return LONG2NUM(val);
23
+ }
24
+
25
+ static VALUE
26
+ ptrace_setoptions(VALUE _mod, VALUE pid, VALUE _addr, VALUE option) {
27
+ if(ptrace(PTRACE_SETOPTIONS, NUM2LONG(pid), NULL, NUM2LONG(option)) != 0)
28
+ perror("ptrace setoptions");
29
+ return Qnil;
30
+ }
31
+
32
+ static VALUE
33
+ ptrace_syscall(VALUE _mod, VALUE pid, VALUE _addr, VALUE sig) {
34
+ if(ptrace(PTRACE_SYSCALL, NUM2LONG(pid), NULL, NUM2LONG(sig)) != 0)
35
+ perror("ptrace syscall");
36
+ return Qnil;
37
+ }
38
+
39
+ static VALUE
40
+ ptrace_traceme(VALUE _mod) {
41
+ if(ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
42
+ perror("ptrace traceme");
43
+ return Qnil;
44
+ }
45
+
46
+ static VALUE
47
+ ptrace_traceme_and_stop(VALUE mod) {
48
+ ptrace_traceme(mod);
49
+ kill(getpid(), SIGSTOP);
50
+ return Qnil;
51
+ }
52
+
53
+
54
+ void Init_ptrace(void) {
55
+ VALUE mSeccompTools = rb_define_module("SeccompTools");
56
+ VALUE mPtrace = rb_define_module_under(mSeccompTools, "Ptrace");
57
+
58
+ /* consts */
59
+ rb_define_const(mPtrace, "EVENT_CLONE", UINT2NUM(PTRACE_EVENT_CLONE));
60
+ rb_define_const(mPtrace, "EVENT_FORK", UINT2NUM(PTRACE_EVENT_FORK));
61
+ rb_define_const(mPtrace, "EVENT_VFORK", UINT2NUM(PTRACE_EVENT_VFORK));
62
+ rb_define_const(mPtrace, "O_TRACECLONE", UINT2NUM(PTRACE_O_TRACECLONE));
63
+ rb_define_const(mPtrace, "O_TRACEFORK", UINT2NUM(PTRACE_O_TRACEFORK));
64
+ rb_define_const(mPtrace, "O_TRACESYSGOOD", UINT2NUM(PTRACE_O_TRACESYSGOOD));
65
+ rb_define_const(mPtrace, "O_TRACEVFORK", UINT2NUM(PTRACE_O_TRACEVFORK));
66
+
67
+ /* ptrace wrapper */
68
+ rb_define_module_function(mPtrace, "geteventmsg", ptrace_geteventmsg, 1);
69
+ rb_define_module_function(mPtrace, "peekdata", ptrace_peekdata, 3);
70
+ rb_define_module_function(mPtrace, "peekuser", ptrace_peekuser, 3);
71
+ rb_define_module_function(mPtrace, "setoptions", ptrace_setoptions, 3);
72
+ rb_define_module_function(mPtrace, "syscall", ptrace_syscall, 3);
73
+ rb_define_module_function(mPtrace, "traceme", ptrace_traceme, 0);
74
+ rb_define_module_function(mPtrace, "traceme_and_stop", ptrace_traceme_and_stop, 0);
75
+ }
76
+
@@ -0,0 +1,8 @@
1
+ # @author david942j
2
+
3
+ # Main module.
4
+ module SeccompTools
5
+ end
6
+
7
+ require 'seccomp-tools/dumper'
8
+ require 'seccomp-tools/version'
@@ -0,0 +1,71 @@
1
+ require 'seccomp-tools/const'
2
+ require 'seccomp-tools/instruction/instruction'
3
+
4
+ module SeccompTools
5
+ # Define the +struct sock_filter+, while more powerful.
6
+ class BPF
7
+ attr_reader :line, :code, :jt, :jf, :k, :arch
8
+ # @return [Array<SeccompTools::Context>]
9
+ attr_accessor :contexts
10
+
11
+ # @param [String] raw
12
+ # One +struct sock_filter+ in bytes, should exactly 8 bytes.
13
+ # @param [Symbol] arch
14
+ # Architecture, for showing constant names in decompile.
15
+ # @param [Integer] line
16
+ # Line number of this filter.
17
+ def initialize(raw, arch, line)
18
+ io = StringIO.new(raw)
19
+ @code = io.read(2).unpack('S').first
20
+ @jt = io.read(1).ord
21
+ @jf = io.read(1).ord
22
+ @k = io.read(4).unpack('L').first
23
+ @arch = arch
24
+ @line = line
25
+ end
26
+
27
+ # Pretty display the disassemble result.
28
+ # @return [String]
29
+ def disasm
30
+ format(' %04d: 0x%02x 0x%02x 0x%02x 0x%08x %s',
31
+ line, code, jt, jf, k, decompile)
32
+ end
33
+
34
+ # @return [Symbol]
35
+ def command
36
+ Const::BPF::COMMAND.invert[code & 7]
37
+ end
38
+
39
+ # @return [String]
40
+ def decompile
41
+ inst.decompile
42
+ end
43
+
44
+ # @param [Context] context
45
+ # Current context.
46
+ # @yieldparam [Integer] pc
47
+ # Program conter after this instruction.
48
+ # @yieldparam [Context] ctx
49
+ # Context after this instruction.
50
+ # @return [void]
51
+ def branch(context, &block)
52
+ # TODO: consider alu
53
+ inst.branch(context).each(&block)
54
+ end
55
+
56
+ private
57
+
58
+ def inst
59
+ @inst ||= case command
60
+ when :alu then SeccompTools::Instruction::ALU
61
+ when :jmp then SeccompTools::Instruction::JMP
62
+ when :ld then SeccompTools::Instruction::LD
63
+ when :ldx then SeccompTools::Instruction::LDX
64
+ when :misc then SeccompTools::Instruction::MISC
65
+ when :ret then SeccompTools::Instruction::RET
66
+ when :st then SeccompTools::Instruction::ST
67
+ when :stx then SeccompTools::Instruction::STX
68
+ end.new(self)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,67 @@
1
+ require 'optparse'
2
+
3
+ module SeccompTools
4
+ module CLI
5
+ # Base class for handlers.
6
+ class Base
7
+ attr_reader :option, :argv
8
+ def initialize(argv)
9
+ @option = {}
10
+ @argv = argv
11
+ end
12
+
13
+ private
14
+
15
+ # Handle show help message.
16
+ # @return [Boolean]
17
+ # For decestors to check if needs to conitnue.
18
+ def handle
19
+ return CLI.show(parser.help) if argv.empty? || %w[-h --help].any? { |h| argv.include?(h) }
20
+ parser.parse!(argv)
21
+ true
22
+ end
23
+
24
+ # Write data to stdout or file(s).
25
+ # @param [String] data
26
+ # Data.
27
+ # @return [void]
28
+ def output(data)
29
+ # if file name not present, just output to stdout.
30
+ return $stdout.write(data) if option[:ofile].nil?
31
+ # times of calling output
32
+ @serial ||= 0
33
+ IO.binwrite(file_of(option[:ofile], @serial), data)
34
+ @serial += 1
35
+ end
36
+
37
+ # Get filename with serial number.
38
+ #
39
+ # @param [String] file
40
+ # Filename.
41
+ # @param [Integer] serial
42
+ # serial number, starts from zero.
43
+ # @return [String]
44
+ # Result filename.
45
+ # @example
46
+ # file_of('a.png', 0)
47
+ # #=> 'a.png'
48
+ # file_of('a.png', 1)
49
+ # #=> 'a_1.png'
50
+ # file_of('test/a.png', 3)
51
+ # #=> 'test/a_3.png'
52
+ def file_of(file, serial)
53
+ suffix = serial.zero? ? '' : "_#{serial}"
54
+ ext = File.extname(file)
55
+ base = File.basename(file, ext)
56
+ File.join(File.dirname(file), base + suffix) + ext
57
+ end
58
+
59
+ # For decestors easy to define usage message.
60
+ # @return [String]
61
+ # Usage information.
62
+ def usage
63
+ self.class.const_get(:USAGE)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,72 @@
1
+ require 'seccomp-tools/cli/disasm'
2
+ require 'seccomp-tools/cli/dump'
3
+ require 'seccomp-tools/version'
4
+
5
+ module SeccompTools
6
+ # Handle CLI arguments parse.
7
+ module CLI
8
+ # Handled commands
9
+ COMMANDS = {
10
+ 'dump' => SeccompTools::CLI::Dump,
11
+ 'disasm' => SeccompTools::CLI::Disasm
12
+ }.freeze
13
+
14
+ # Main usage message.
15
+ USAGE = <<EOS.sub('%COMMANDS', COMMANDS.map { |k, v| "\t#{k}\t#{v::SUMMARY}" }.join("\n")).freeze
16
+ Usage: seccomp-tools [--version] [--help] <command> [<options>]
17
+
18
+ List of commands:
19
+
20
+ %COMMANDS
21
+
22
+ See 'seccomp-tools --help <command>' to read about a specific subcommand.
23
+ EOS
24
+
25
+ module_function
26
+
27
+ # Main work method for CLI.
28
+ # @param [Array<String>] argv
29
+ # Command line arguments.
30
+ # @return [void]
31
+ # @example
32
+ # work(argv: %w[--help])
33
+ # #=> # usage message
34
+ # work(argv: %w[--version])
35
+ # #=> # version message
36
+ def work(argv)
37
+ # all -h equivalent to --help
38
+ argv = argv.map { |a| a == '-h' ? '--help' : a }
39
+ idx = argv.index { |c| !c.start_with?('-') }
40
+ preoption = idx.nil? ? argv.shift(argv.size) : argv.shift(idx)
41
+
42
+ # handle --version or --help or nothing
43
+ return show("SeccompTools Version #{SeccompTools::VERSION}") if preoption.include?('--version')
44
+ return show(USAGE) if idx.nil?
45
+
46
+ # let's handle commands
47
+ cmd = argv.shift
48
+ argv = %w[--help] if preoption.include?('--help')
49
+ return show(invalid(cmd)) if COMMANDS[cmd].nil?
50
+ COMMANDS[cmd].new(argv).handle
51
+ end
52
+
53
+ # Just write message to stdout.
54
+ # @param [String] msg
55
+ # The message.
56
+ # @return [false]
57
+ # Always return +false+.
58
+ def show(msg)
59
+ puts msg
60
+ false
61
+ end
62
+
63
+ class << self
64
+ private
65
+
66
+ # Invalid command message.
67
+ def invalid(cmd)
68
+ format("Invalid command '%s'\n\nSee 'seccomp-tools --help' for list of valid commands", cmd)
69
+ end
70
+ end
71
+ end
72
+ end