seccomp-tools 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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