zed.0xff.me

Заметки о линуксе, Ruby, Rails & so on.

RuCTF 2010 Quals: отчет

Случайно и неожиданно попал на “квалификационный заезд” RuCTF 2010 в составе команды Lobotomy.
Буквально так:

1
2
3
4
5
6
7
8
9
linkoln: свободен?
zed:     смотря для чего )
linkoln: тут мы в ructf участвуем, может глянешь пару задачек
zed:     ок давай
.......
zed:     решил реверс100 ^__^ !
.......
zed:     решил реверс200 ^____^ ! вписывайте меня в команду! :)
linkoln: ок, все только за :)

Дело это продолжалось двое суток, участвовало порядка 40 команд.
Всего было 7 категорий задач, по 5 задач в каждой, от 100 до 500 баллов за решение.

результаты

Скажу сразу что мы заняли почетное 9-е место.. причем нас таких команд получилось аж 3:

1 SiBears 6800
2 Smoked_Chicken 6600
3 CIT 6300
4 Bushwhackers 6100
5 Vulnbugs 5600
6 x0r 5200
7 KEVA 5000
7 Space_Invaders 5000
9 Quinque 4900
9 ForkingBugs 4900
9 Lobotomy 4900

Проходящими в финал по правилам являются “10 лучших команд”, так что, видимо, в финал мы прошли.. но еще не факт.

задачи

= Crypto100 =
расшифровать “S[bgXGO DVG JT UJF CHVU DVG PO UJF XSXPE@”
алгоритм: вычесть из каждой первой и последней буквы слова 1, из остальных букв слова вычесть больше чем 1 :) четкого алгоритма не нашел
ответ: RUSSIAN CTF IS THE BEST CTF ON THE WORLD

= Forensics500 =
дан образ диска злоумышленника [ссылка], известно что на нем спрятан пароль от аккаунта “[Dervish]”, найти md5 этого пароля
решение: на диске нет загрузочных систем, но есть MBR и GPT партиции. берем gdisk (аналог fdisk, но с поддержкой GPT) и начинаем выковыривать из образа разделы. на разделах лежат картинки, являющиеся частями одного большого изображения, на котором ручкой написаны разные пароли, в т.ч. искомый.
главная подляна в том, что последний кусок запрятан в FAT разделе, в удаленном(!) файле, да еще и в совершенно ##баническом формате SCT

= Reverse300 =
дан кейген [ссылка], нужно сгенерить ключ для юзера %username%. проблема в том что keygen expired и работать отказывается.
решение: всё просто. берем hiew, правим в кейгене условный переход по проверке даты на безусловный, запускаем, генерим

= Reverse400 =
дан файл omar.plc [ссылка], нужно найти все числа между 1300 и 1500 где алгоритм неточен.
решение: данный файл – это скомпиленный перловый байткод, в чем можно убедиться заглянув внутрь файла, и погуглив на “ByteLoader”.
далее проблема была найти такую машину, где бы и архитектура машины, и версия перла совпадала бы с той, на которой код был скомпилен: x86_64-linux-thread-multi.0.07
машину я нашел, но очень не сразу. код на ней запустился (просто perl omar.plc XXXX, где XXXX – это входное число)
программа на выходе выдает 0 или 1, в зависимости от чисел.
погуглив на слово Omar можно догадаться что это имеет отношение к Омару Хайяму, а внимательно посмотрев на числа, на которые программа выдает “1” (4,8,12,16,20,15,29,33,…) можно предположить что это что-то типа алгоритма вычисления високосного года.
дальше по гуглу и педивикии смотрим какой там календарь придумал Хайям, и как его считать, и сравниваем с данными программы.
тут был некоторый косяк в том что на все мои попытки чекер возвращал что результат неверный, а потом орги зачли задачу вручную.. так что хз какой именно был правильный ответ

= Stegano100 =
текст задачи: “Поехали! Вот только скажите, куда [ссылка] ?”
решение:
а) выписываем регионы в порядке соответсвующих им чисел
б) выписываем рядом с каждым регионом его автомобильный госномер http://bit.ly/bwzDfJ
в) замечаем что несколько раз повторяется 32 (ASCII пробел) и оканчивается на 33 (воскл.знак)
г) конвертим числа в буквы по ASCII
д) получаем “WELCOME TO RUCTF 2010!”

= Stegano300 =
расшифруйте сообщение: message [ссылка]
решение (слабонервным не смотреть!): оооооооооооочень жестокая задача… файлик надо было догадаться тупо переименовать в *.com и запустить под dos-compatible системой…. >_<
…а я его в base64 сначала декодил (причем он декодировался без ошибок) и пытался дальше gzip-ом распаковывать, предварительно приклеив gzip-хидер..

= CTB400 =
злоумышленник проник в нашу систему, и оставил бекдор. найдите этот бекдор.
хинт: злоумышленник ядро не трогал.
образ системы [ссылка] (vmware image, 400Mb), или с торрента, если еще жив [тоже ссылка]
решение: внутри – Debian 5.0.4 minimal + sshd + apache + kernel sources + всякие dev либы. пароль рута неизвестен.
сначала натравил на образ strings, грепал на backdoor, passwd, root – ничего особо не нашел.
потом поставил его в VMWare Player, загрузился, понял, что паролей у меня нет :), качнул system rescue cd, загрузился с него, но пароли менять не стал, так с него и изучал.
примечательно что в системе удалена вся история команд рута, а также всем файлам в системе сделан touch на одну и ту же дату :)
поковыряв систему так ничего и не нашел.
качнул Debian 5.0.4, поставил в соседнюю VM, сделал find / type f -ls на обеих машинах, и скриптом вычел один список из другого. получил список файлов с первой ВМ, отличающихся от оригинальных, либо отсутствующих в свежем истоле. сначала грешил на sudo или измененный initrd.. но потом он спалился на
/etc/apache2/mods-available/security.conf 65
/etc/apache2/mods-available/security.load 72
/usr/lib/apache2/modules/mod_security.so 7029
:) внутри модуль который ловит GET запросы на /hack, причем http request header должен быть ructf/2010 :)
telnet 10.2.3.4 80
GET /hack/etc/ ructf/2010\n\n
GET /hack/root/secret.tar.gz ructf/2010\n\n
в ответ возвращает либо список файлов в каталоге, если URI заканчивается на “палку”, либо содержимое запрошенного файла

mysql: пробелы без пробелов

хитрый ход для ситуаций, когда пробел поставить нельзя, но ооооооооочень хочется:

1
2
3
4
5
6
7
mysql> select/**/count(*)/**/from/**/users;
+----------+
| count(*) |
+----------+
|        7 | 
+----------+
1 row in set (0.01 sec)

// подсмотрен в процессе участия в RuCTF 2010 quals :)

fork() в нужный момент экономит время

rspec_bisect и с чем его едят

Есть у меня скриптик rspec_bisect, предназначенный для выискивания в rails-проекте спеков, которые в процессе своего выполнения что-то глобально меняют/трогают/expect-ают или stub-ают (а то и, даже страшно сказать, any_instance-ют!..) в результате чего потом другие спеки успешно натыкаются на расставленные грабли, и FAIL-ятся с довольно фантастическими ошибками.

Показанием к применению данного скрипта можно считать случай когда запуск одной спеки через spec /spec/models/first_model_spec.rb завершается успешно и без FAIL-ов, а на запуск ее же вместе с другими спеками через rake spec получаем гарантированные FAIL-ы.

Пример работы rspec_bisect.

причем здесь fork() ?..

Притом что изначально для поиска иголки в стоге сена я запускал наборы спеков через `spec /spec/models/m1_spec.rb spec/models/m2_spec.rb ...`, т.е. через вызов system() (чтобы получить изолированное окружение, не влияющее ни на родителя, ни на соседей).

При этом каждый раз рельсы упорно грузили весь свой арсенал, каждый раз отъедая что-то около 10с (на довольно тяжелом проекте).

Зерном идеи оптимизации послужила давняя попытка запуска Spork. Почему-то она не никаким приростом скорости не увенчалась, но полезный осадок в виде особенностей работы Kernel.fork оставила.

Суть в том (если кто не знает), что “fork() creates a new process by duplicating the calling process.” © man 2 fork. Т.е. если вовремя форкнуться, то не надо 10 раз грузить весь Rails environment, достаточно будет загрузить его всего один раз, а все запуски тестов делать уже в fork-нутых child-ах.

На том и порешили. После непродолжительной борьбы rspec сдался и раскололся чем и как он запускает спеки ( оказалось – через at_exit.. старый извращенец :), и был слегка поmonkeyпатчен, чтоб не делал лишних телодвижений.

Сравнение запусков “до” и “после”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
### ДО ###
[.] running   62 specs.. Done. ( 19s) (105/0/4) : target OK
[.] running   63 specs.. Done. ( 60s) (384/2/6) : target FAIL
[.] running   32 specs.. Done. ( 35s) (157/0/2) : target OK
[.] running   32 specs.. Done. ( 47s) (236/2/4) : target FAIL
[.] running   16 specs.. Done. ( 35s) (129/2/1) : target FAIL
[.] running    8 specs.. Done. ( 28s) (73/0)    : target OK
[.] running    9 specs.. Done. ( 22s) (65/2/1)  : target FAIL
[.] running    5 specs.. Done. ( 18s) (52/2/1)  : target FAIL
[.] running    3 specs.. Done. ( 17s) (33/0)    : target OK
[.] running    3 specs.. Done. ( 17s) (28/2/1)  : target FAIL
[.] running    2 specs.. Done. ( 17s) (27/2/1)  : target FAIL

### ПОСЛЕ ###
[.] running   62 specs.. Done. (  9s) (105/0/4) : target OK
[.] running   63 specs.. Done. ( 49s) (384/2/6) : target FAIL
[.] running   32 specs.. Done. ( 21s) (157/1/2) : target OK
[.] running   32 specs.. Done. ( 34s) (236/3/4) : target FAIL
[.] running   16 specs.. Done. ( 24s) (129/3/1) : target FAIL
[.] running    8 specs.. Done. ( 20s) (73/1)    : target OK
[.] running    9 specs.. Done. (  9s) (65/3/1)  : target FAIL
[.] running    5 specs.. Done. (  8s) (52/3/1)  : target FAIL
[.] running    3 specs.. Done. (  4s) (33/1)    : target OK
[.] running    3 specs.. Done. (  8s) (28/3/1)  : target FAIL
[.] running    2 specs.. Done. (  7s) (27/3/1)  : target FAIL

В результате время запуска тестового набора сократилось с 315с до 193с, т.е. чуть больше чем на треть.

magenta?.. there's no magenta!

Интересные штуки, оказывается, вытворяет наше зрение..

1. первая картинка

концентрируем взгляд на точке в центре. стараемся не моргать. (не более минуты)

эффект: все цвета должны исчезнуть, останется только серый фон.

2. вторая картинка

смотрим на крест в середине.

эффект: розовые круги исчезнут, останется один бегающий зеленый круг.

3. ссылки и факты

Thanks to abbra and Magenta Ain’t A Colour, or another link: Magenta Ain’t A Colour

rails3: link_to + image_tag

было: (rails 2.x)

1
2
3
<%= link_to image_tag('rainbow.png'), '/' -%>

<%= link_to "#{image_tag('rainbow.png')}Главная", '/' -%>

стало: (rails 3.x)

1
2
3
<%= link_to image_tag('rainbow.png'), '/' -%>

<%= link_to "#{image_tag('rainbow.png')}Главная".html_safe, '/' -%>

Первый вариант (просто image_tag) не изменился, а вот второй вариант (image_tag внутри строки) теперь требует явного указания html_safe.

rails3: link_to_function

1
ActionView::Template::Error (undefined method `link_to_function' for #<Class>)

теперь link_to_function находится в плагине prototype_legacy_helper:

1
./script/rails plugin install git://github.com/rails/prototype_legacy_helper.git

возможно, ребята придумали чем-то заменить, а потом и задепрекейтить, но никаких постов на эту тему я в нете не нашел.

rspec bisect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#./rspec-bisect.rb spec/**/*_spec.rb spec/controllers/spaces/tickets_controller_spec.rb
[.] rspec runner: ./script/spec
[.] target spec : spec/controllers/spaces/tickets_controller_spec.rb
[.] 123 candidate specs
[.] running   62 specs.. Done. ( 19s) (105/0/4)        : target OK
[.] running   63 specs.. Done. ( 64s) (386/2/6)        : target FAIL
[.] running   32 specs.. Done. ( 34s) (157/0/2)        : target OK
[.] running   32 specs.. Done. ( 43s) (238/2/4)        : target FAIL
[.] running   16 specs.. Done. ( 38s) (129/2/1)        : target FAIL
[.] running    8 specs.. Done. ( 27s) (73/0)           : target OK
[.] running    9 specs.. Done. ( 19s) (65/2/1)         : target FAIL
[.] running    5 specs.. Done. ( 19s) (52/2/1)         : target FAIL
[.] running    3 specs.. Done. ( 16s) (33/0)           : target OK
[.] running    3 specs.. Done. ( 18s) (28/2/1)         : target FAIL
[.] running    2 specs.. Done. ( 17s) (27/2/1)         : target FAIL
[*] found matching spec: spec/models/mailman_spec.rb

Download @ github.

Rails 3.x "Crazy Loading" is awesome!

1
2
3
4
5
6
7
8
red_items = Item.where(:colour => 'red')
red_items.find(1)
item = red_items.new
item.colour #=> 'red'

red_items.exists? #=> true
red_items.update_all :colour => 'black'
red_items.exists? #=> false

// actually it’s “Lazy Loading” and stuff. Read more: Active Record Query Interface 3.0.

Rails streaming VS mongrel, thin, ebb and Passenger

С некоторых пор моим любимым веб-сервером для руби приложений является thin.
Но сегодня он меня конкретно разочаровал. Как, впрочем и mongrel.

Как нетрудно догадаться из заголовка, дело касается streaming
(про стриминг в рельсах читать тут, начиная с Streaming data and/or controlling the page generation)
(и почему они не автогенерят id для хидеров?? можно было бы ссылку сразу куда надо поставить..)

пример там приведен такой:

1
2
3
4
5
6
7
  # Streams about 180 MB of generated data to the browser.
  render :text => proc { |response, output|
    10_000_000.times do |i|
      output.write("This is line #{i}\n")
      output.flush
    end
  }

так вот, что thin, что mongrel, оба тупо забивают на этот стриминг, и пытаются всосать в себя всё что им рельсы отдают, а потом выплюнуть юзеру единым куском..
thin, например, делает так:

1
2
terminate called after throwing an instance of 'std::runtime_error'
  what():  no allocation for outbound data

а mongrel так:

1
2
Error calling Dispatcher.dispatch #<NoMemoryError: failed to allocate memory>
/usr/lib64/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/cgi_process.rb:58:in `write'

Еще хотел быстренько попробовать ebb, но не обнаружил в дистрибутиве внятных инструкций по его установке и настройке.. ладно, запустил через Ebb.start_server("/path/to/rails/app"), но толку от этого оказалось мало – на порту он поднялся, но ни на один запрос отвечать не захотел.. тупо висел и думал о чем-то там своем..

And the winner is…

Passenger. Хоть я его раньше и не использовал, и довольно таки скептически к нему относился, но он успешно зарекомендовал себя в продакшене, легко поставился (потянув, естественно за собой апача, которого я тоже недолюбливаю..) (хмм.. хотя он есть и для nginx, это несколько меняет дело, на досуге поковыряю)

Так вот, Passenger легко отдал псевдофайлик размером 2 гига с локалхоста на локалхост со скоростью около 12 мегабайт в секунду. При этом не показав никакого значительного увеличения потребления памяти!

PS:

  • Linux zz 2.6.31-gentoo-r6-zz #1 SMP PREEMPT Tue Dec 22 01:38:46 YEKT 2009 x86_64 Intel(R) Core(TM)2 Duo CPU E8400 @ 3.00GHz GenuineIntel GNU/Linux
  • Rails 2.3.5
  • mongrel 1.1.5
  • thin 1.2.5
  • passenger 2.2.9

You MUST free memory you got from ALLOC_N & friends

It’s not documented anywhere, but you must call “xfree()” (note ‘x’ there) on memory blocks you got from ALLOC_N & friends.
Ruby will not free that memory automatically during it’s usual GC process.

Guys from ruby-talk and rubyforge also noticed that.

sample code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
VALUE method_parse(VALUE self, VALUE text) {
    VALUE s;
    char *p    = RSTRING(text)->ptr;
    in_buf_len = RSTRING(text)->len;

    // [skipped some code here]

    buf = ALLOC_N(char, bufsize); 

    // protect buf from GC (theoretically)
    rb_iv_set(self,"@obj",Data_Wrap_Struct(rb_cData,NULL,NULL,buf));

    // [skipped some hard work here]

    // make ruby string from our char[] data
    s = rb_str_new(buf,bufptr-buf);

    // cleanup
    rb_iv_set(self,"@obj",Qnil);
    xfree(buf);
    buf  = NULL;
    bufsize = 0;

    return s;
}

benchmark: Ruby's rb_str_cat() vs C's strcat()

В связи с началом написания расширений для ruby решил провести бенчмарк рубевской функции rb_str_cat() VS сишной родной strcat()
Результат меня немало удивил:

1
2
strcat     3.360s
rb_str_cat 0.002s

Тесты проводились в тяжелых полевых условиях – испытуемым надо было 100000 (сто тысяч) раз приклеить к строке(изначально пустой) один заранее заданный символ.
И ruby сделал это, грубо говоря, в 1500 раз быстрее!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define N 100000

VALUE test_concat_rb_str_cat(VALUE self){
    int i;
    VALUE s = rb_str_new2("");

    for(i=0;i<N;i++) rb_str_cat(s,"t",1);
    return s;
}

VALUE test_concat_strcat(VALUE self){
    int i;
    char buf[N+2];

    *buf=0;
    for(i=0;i<N;i++) strcat(buf,"t");
    return rb_str_new2(buf);
}

На самом деле никакого подвоха тут нет – просто в руби объект “строка” содержит в себе не только указатель на байты содержимого, но и длину строки тоже, а сишный вариант при этом каждый раз считает длину строки. Т.е. ему, бедному, пришлось перемолотить при этом суммарно почти 5 гигабайт, каждый раз считая длину строки начиная с самого первого символа, и до 100-тысячного в конце.

Прелесть руби здесь в том что не надо думать о памяти – он сам выделит сколько надо и освободит когда будет можно. В сях это слегка сложнее, но можно доработать сишный код так, чтобы он и память выделял динамически, и длину строки стопицот раз не пересчитывал. Например, так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
VALUE test_concat_char(VALUE self){
    int i;
    int bufsize = 0x1000;
    char *buf = malloc(bufsize);
    char *p=buf;
    VALUE s;

    for(i=0;i<N;i++){
        *p++ = 't';
        if( p-buf >= bufsize ){
            char *oldbuf = buf;
            bufsize += 0x1000;
            buf = realloc(buf, bufsize);
            p = buf + (p-oldbuf);
        }
    }
    s=rb_str_new(buf,p-buf);
    free(buf);
    return s;
}

Скорость обработки получается уже совсем другая:

1
2
3
strcat     3.36732s
rb_str_cat 0.00215s
char       0.00013s

Вот теперь сишный код в 16 раз быстрее чем руби, и в 24000 с лишним раз быстрее, чем в начале.

PS: это всё на Linux 2.6.31-gentoo-r6-zz x86_64 Intel(R) Core(TM)2 Duo CPU E8400 @ 3.00GHz

пишем на C расширение для ruby

Это не просто, а очень просто (как два пальца об асфальт два файла написать):

1й файл: mytest.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "ruby.h"
void Init_mytest();
VALUE method_sayhello(VALUE);

VALUE mytest = Qnil;

void Init_mytest() {
  mytest = rb_define_module("MyTest");
  rb_define_method(mytest, "sayhello", method_sayhello, 0);
}

VALUE method_sayhello(VALUE self) {
  puts("Hello World!");
  return Qnil;
}

2й файл: extconf.rb

1
2
3
4
require 'mkmf'
extension_name = 'mytest'
dir_config(extension_name)
create_makefile(extension_name)

запуск:

1
2
3
4
5
6
7
8
9
10
11
12
#ruby extconf.rb 
creating Makefile
#make
x86_64-pc-linux-gnu-gcc -shared -o mytest.so mytest.o -L. -L/usr/lib64 -Wl,-R/usr/lib64 -L. -Wl,-O1 -rdynamic -Wl,-export-dynamic    -Wl,-R -Wl,/usr/lib64 -L/usr/lib64 -lruby18  -lpthread -lrt -ldl -lcrypt -lm   -lc
#irb
irb(main):001:0> require 'mytest'
=> true
irb(main):002:0> include MyTest
=> Object
irb(main):003:0> sayhello
Hello World!
=> nil

// пример содран с Введения в расширения Ruby на C, но там всё разжевывается под винду и вижуалстудию

спамеры научились подбирать SASL пароли

Что-то стал почтовик туго работать..
Смотрю – loadavg около 3.8 .. что-то тут неладно..
Смотрю tail -f /var/log/maillog – там жуть полнейшая.. всё скачет и мелькает сотнями строк в секунду..
Причем чаще всего строчки вида:

host mailin-03.mx.aol.com[205.188.109.56] refused to talk to me: 554 IP:y.y.y.y – A problem occurred. (Ask your postmaster for help or to contact tosa@rx.t-online.de to clarify.)

или такие:

host c.mx.mail.yahoo.com[209.191.88.254] refused to talk to me: 421 4.7.1 [TS03] All messages from y.y.y.y will be permanently deferred; Retrying will NOT succeed. See http://postmaster.yahoo.com/421-ts03.html

или такие:

status=bounced (host mxs.mail.ru[94.100.176.20] said: 550 spam message discarded. Please visit http://mail.ru/notspam/ or report details to abuse@corp.mail.ru. Error code: 0F9B7FC84BF25CB6A821A57C9B6112E28F9CA260D04BD65B (in reply to end of DATA command))

в /var/spool/postfix – 180тыс сообщений.. O_o

В итоге оказалось что спамеры подобрали пароль на акк вида test:test (явно кто-то из админов заводил для теста..) и вовсю спамили с него везде куда их черная душа пожелала..

В сухом остатке:

  • сменил пароль юзера на непотребство вида “gbplf[eq” :)
  • жестко забанил спамеров:
1
2
3
4
  ipfw table 10 add 82.127.122.36
  ipfw table 10 add 203.161.99.96
  ipfw table 10 add 165.228.2.249
  ipfw add deny ip from "table(10)" to me 
  • ограничил количество одновременных SMTP-коннектов 5-ю штуками:
1
  ipfw add allow ip from any to me 25 setup limit src-addr 5
  • запустил чистилку на /var/spool/postfix:
1
2
  cd /var/spool/postfix
  grep -rlF "sender: test" . | xargs -L 100 rm

работает небыстро, зато надежно :) уже вычистила около 30K сообщений.
ключик “-L 100” нужен для того чтобы rm вызывался не в самом-самом конце поиска, а на каждые N найденных файлов.

инвайты на google wave

Раздам инвайты на google wave.
Есть 8 штук.
Вдруг еще у кого нет :)

халява: ifolder.ru OR last.fm OR скайп

отдам бесплатно в хорошие руки:

  1. пополнение баланса скайп на что-то около 150руб
  2. оплаченный акк на ifolder.ru
  3. оплаченный акк на last.fm

ОДНО из трех! а если точнее – то это был такой мелкий приз на newburn.ru, ну и мне собсно ни то ни то ни то не особо и надо..
так что отдам бесплатно :)

upd: халява закончилась кто не успел – тот опоздал