[GoogleCTF quals 2021] weather

2022. 4. 9. 19:47CTF/ㅁㄹ

얼마 못 본 문제

main 함수에서 처음 보는 서식 지정자를 쓴다

 

ida freeware

register_printf_function으로 서식 지정자를 선언해줄 수 있다고 한다

 

T에 들어가는 sub_225A를 보면

__int64 __fastcall sub_225A(FILE *stream, const struct printf_info *info, const void *const *args)
{
  return (unsigned int)fprintf(stream, "%d%s", ***(unsigned int ***)args, *(const char **)(**(_QWORD **)args + 8LL));
}

요래 되어있어서

 

입력을 London을 주면

if ( !strcmp("London", s2) )
{
    v10 = 5;
    v11 = "W";
    v7 = "rain";
    v8 = 1337;
    v9 = "mm";
    v5 = 10; // rbp - 0x40
    v6 = "°C"; // rbp - 0x38
}
...
printf("Temperature: %T\n", &v5);
...
Welcome to our global weather database!
What city are you interested in?
London
Weather for today:
Precipitation: 1337mm of rain
Wind: 5km/h W
Temperature: 10°C
Flag: none

이렇게 10°C로 나옴

 

flag를 찾아야되니까 %F를 봐야될 것 같다

 

이거 앞에서 안쓰던데 어케 되는거지

 

1. %F

return (unsigned int)fprintf(stream, "%52C%s", **(_QWORD **)args, a4);

 

%52C를 모름

 

2. %C

info가 뭐고

 

struct printf_info
{
  int prec;			/* Precision.  */
  int width;			/* Width.  */
  wchar_t spec;			/* Format letter.  */
  unsigned int is_long_double:1;/* L flag.  */
  unsigned int is_short:1;	/* h flag.  */
  unsigned int is_long:1;	/* l flag.  */
  unsigned int alt:1;		/* # flag.  */
  unsigned int space:1;		/* Space flag.  */
  unsigned int left:1;		/* - flag.  */
  unsigned int showsign:1;	/* + flag.  */
  unsigned int group:1;		/* ' flag.  */
  unsigned int extra:1;		/* For special use.  */
  unsigned int is_char:1;	/* hh flag.  */
  unsigned int wide:1;		/* Nonzero for wide character streams.  */
  unsigned int i18n:1;		/* I flag.  */
  unsigned int is_binary128:1;	/* Floating-point argument is ABI-compatible
				   with IEC 60559 binary128.  */
  unsigned int __pad:3;		/* Unused so far.  */
  unsigned short int user;	/* Bits for user-installed modifiers.  */
  wchar_t pad;			/* Padding character.  */
};

비트플래그

0x01 : is_long_double (L)

0x02 : is_short (h)

0x04 : is_long (l)

0x08 : alt (#)

0x10 : space ( )

0x20 : left (-)

0x40 : showsign (+)

...

 

width가 7이면

%3.1hM%3.0lE%+1.3lM%1.4llS%3.1lM%3.2lO%-7.3C

 

52면

%0.4096hhM%0.255llI%1.0lM%1.8llL%0.1lU%1.0lM%1.16llL%0.1lU%1.200llM%2.1788llM%7C%-6144.1701736302llM%0.200hhM%0.255llI%0.37llO%0200.0C

 

너무 많아보이는데


[+] gdb에서

아무 printf_function에다 breakpoint걸고 p *(struct printf_info*)$rsi 해봄

 

1.23A라 하면

info->prec = 23

info->width = 1

info->spec = 'A'

 

이런식으로 들어감

 

[+] %52C에서 다 돌면 unk_6880에 값이 들어가고

%s에서 이게 출력되는듯


%C를 봤으니까 하나씩 봄

 

%M ~ %U까지 함수가 다 비슷하게 생겼다

 

[+] %M

*v7 = v6

 

[+] %S

*v7 += v6

 

[+] %O

*v7 -= v6

 

[+] %X

*v7 *= v6

 

[+] %V
*v7 /= v6

 

[+] %N

*v7 %= v6

 

[+] %L

*v7 <<= v6

 

[+] %R

*v7 >>= v6

 

[+] %E

*v7 ^= v6

 

[+] %I

*v7 &= v6

 

[+] %U

*v7 |= v6

 

이고

 

width = info->width;
prec = info->prec;
if ( (*((_BYTE *)info + 12) & 0x20) != 0 )      // -
{
	v7 = (int *)&a52cS[width];
}
else if ( (*((_BYTE *)info + 12) & 0x40) != 0 ) // +
{
	v7 = (int *)&a52cS[dword_70A0[width]];
}
else
{
	v7 = &dword_70A0[width];
}

v6 = 0;
if ( (*((_BYTE *)info + 13) & 2) != 0 )         // hh
{
	v6 = *(_DWORD *)&a52cS[prec];
}
else if ( (*((_BYTE *)info + 12) & 2) != 0 )    // h
{
	v6 = *(_DWORD *)&a52cS[dword_70A0[prec]];
}
else if ( (*((_BYTE *)info + 12) & 1) != 0 )    // L
{
	v6 = info->prec;
}
else if ( (*((_BYTE *)info + 12) & 4) != 0 )    // l
{
	v6 = dword_70A0[prec];
}

비트플래그로 v7, v6 설정

 

1. 0.4096hhM

*(_DWORD *)&dword_70A0[0] = *(_DWORD *)&a52cS[4096] 

입력한 도시 4바이트가 dword_70A0에 들어감

 

2. 0.255llI

ll로 하면 is_long_double 플래그가 1이 됨

*(_DWORD *)&dword_70A0[0] &= 255    // 첫바이트만 남김

 

3. 1.0lM

*(_DWORD *)&dword_70A0[1] = dword_70A0[0]

 

4. 1.8llL

*(_DWORD *)&dword_70A0[1] <<= 8

 

5. 0.1lU

*(_DWORD *)&dword_70A0[0] |= dword_70A0[1]

 

6. 1.0lM

*(_DWORD *)&dword_70A0[1] = dword_70A0[0]

 

7. 1.16llL

*(_DWORD *)&dword_70A0[1] <<= 16

 

8. 0.1lU

*(_DWORD *)&dword_70A0[0] |= dword_70A0[1]

 

9. 1.200llM

*(_DWORD *)&dword_70A0[1] = 200

 

10. 2.1788llM

*(_DWORD *)&dword_70A0[2] = 1788

 

11. 7C

으악

%3.1hM%3.0lE%+1.3lM%1.4llS%3.1lM%3.2lO%-7.3C

 

11-1. 3.1hM

*(_DWORD *)&dword_70A0[3] = &a25cS[dword_70A0[1]]  // *($code + 0x5080 + 200)

 

11-2. 3.0lE

*(_DWORD *)&dword_70A0[3] ^= dword_70A0[0]

 

11-3. +1.3lM

*(_DWORD *)&a25cS[dword_70A0[1]] = dword_70A0[3]     // 11-1에서 뽑아온 자리에 xor한 결과 넣음

 

11-4. 1.4llS

*(_DWORD *)&dword_70A0[1] += 4

 

11-5. 3.1lM

*(_DWORD *)&dword_70A0[3] = dword_70A0[1]

 

11-6. 3.2lO

*(_DWORD *)&dword_70A0[3] -= dword_70A0[2]     // 204 - 1788

 

11-7. -7.3C

dword_70A0[3] < 0이면 계속돌음

 

나왔다치고

 

12. -6144.1701736302llM

*(_DWORD *)&a52cS[6144] = 1701736302

얘가 unk_6880에 none 넣는거

 

13. 0.200hhM

*(_DWORD *)&dword_70A0[0] = *(_DWORD *)&a52cS[200]

 

14. 0.255llI

*(_DWORD *)&dword_70A0[0] &= 255

 

15. 0.37llO

*(_DWORD *)&dword_70A0[0] -= 37

 

16. 0200.0C

그냥 지나가면 시작이 printf("Flag: %F\n", unk_6880) -> %52C%s 였어서 none이 나옴

 

dword_70A0[0] == 0 이 되게 해서

fprintf(stream, &a52cS[200])가 호출돼야 함

 

-> %0200.0C 에서

else
{
  v5 = info->pad != '0' || dword_70A0[prec] == 0;
}
if ( v5 )
  fprintf(stream, &a52cS[info->width]);

pad가 '0'이니까 bruteforce로 dword_70A0[prec] == 0이 되게하는 첫번째 글자를 찾으면 됨

 

# stage1.py

# gdb -x stage1.py
import gdb
import string

gdb.execute("file ./weather")
gdb.execute("start")
gdb.execute("code")
gdb.execute("b*$code+0x214c")

for c in string.printable:
    print(f"[-] {c}")
    gdb.execute(f"r <<< {c} > /dev/null")
    
    if gdb.parse_and_eval("$eax") == 0:
        print(f"[+] found! {c}")
        gdb.execute("q")
        break

첫번째 글자는 T

 

이때 &a52cS[200]에 서식지정자는

%4.5000llM%0.13200llM%337C%0.0llM%500C%1262C%0653.0C

 

1. 4.5000llM

*(_DWORD *)&dword_70A0[4] = 5000 (0x1388)

 

2. 0.13200llM

*(_DWORD *)&dword_70A0[0] = 13200 (0x3390)

 

3. 337C

?

 

4. 0.0llM

*(_DWORD *)&dword_70A0[0] = 0

 

5. 500C

?

 

6. 1262C

?

 

7. 0653.0C

?

 

노가다로는 쉽지 않아 보인다

 

# parse.py

from pwn import *
sys.setrecursionlimit(10**9)

with open("./weather", "rb") as f:
    e = f.read()

ipt = [ord('T')] + [0]*0x2000

S = list(e[0x4080:0x5080])
S += ipt

D = [0]*32

def parse(s: str, debug=False):
    result = []
    s = s.split('%')[1:]

    for _format in s:
        res = ''
        v6 = ''
        v7 = ''
        width = ''
        prec = ''
        sign = ''
        spec = _format[-1]
        _format = _format[:-1]
        v7d = v6d = False

        if _format[0] == '-':
            sign = '-'
            v7 = 'S[{}]'
            _format = _format[1:]
        elif _format[0] == '+':
            sign = '+'
            v7 = 'S[D[{}]]'
            v7d = True
            _format = _format[1:]
        else:
            sign = _format[0]
            v7 = 'D[{}]'
            v7d = True

        if spec == 'C':
            if '.' in _format:
                width, prec = map(int, _format.split('.'))
            else:
                width, prec = int(_format), 0
            
            res = f'C S[{width}] {prec*4} {sign}'
            result.append(res)
            continue
        elif spec == 'M':
            op = '='
        elif spec == 'S':
            op = '+='
        elif spec == 'O':
            op = '-='
        elif spec == 'X':
            op = '*='
        elif spec == 'V':
            op = '//='
        elif spec == 'N':
            op = '%='
        elif spec == 'L':
            op = '<<='
        elif spec == 'R':
            op = '>>='
        elif spec == 'E':
            op = '^='
        elif spec == 'I':
            op = '&='
        elif spec == 'U':
            op = '|='

        if 'hh' in _format:
            v6 = 'S[{}]'
            _format = _format[:-2]
        elif 'h' in _format:
            v6 = 'S[D[{}]]'
            v6d = True
            _format = _format[:-1]
        elif 'll' in _format:
            v6 = '{}'
            _format = _format[:-2]
        elif 'l' in _format:
            v6 = 'D[{}]'
            v6d = True
            _format = _format[:-1]
        else:
            print(f"[!] format error : {_format}")
            exit()
        
        width, prec = map(int, _format.split("."))

        if v7d: width *= 4
        if v6d: prec *= 4

        res = ' '.join([v7.format(width), op, v6.format(prec)])
        result.append(res)

    if debug:
        print('\n'.join(result))
    return result

# C S[width] prec sign(pad)
def execute(x, op, y=None, sign=None):
    global S, D
    if x == 'C':
        check = False
        idx = int(y)
        d = int.from_bytes(bytes(D[idx:idx+4]), 'little')
        if sign == '-':
            check = d >= 0x80000000
        elif sign == '+':
            check = d < 0x80000000
        else:
            check = sign != '0' or d == 0

        if check:
            idx = int(op[2:-1])
            _s = bytes(S[idx:]).split(b'\x00')[0].decode()
            if _s == "%4.5000llM%0.13200llM%337C%0.0llM%500C%1262C%0653.0C":
                debug()
                dump()
                exit()
            res = parse(_s, False)
            for _s in res:
                execute(*_s.split())

        return
    xx = yy = 0
    x_idx = y_idx = 0
    if '[' in x[2:-1]:
        x_idx = int(x[2:-1][2:-1])
        x_idx = eval(f"{x[2:-1][:2]}x_idx:x_idx+4]")
        x_idx = int.from_bytes(bytes(x_idx), 'little')
    else:
        x_idx = eval(f"{x[2:-1]}")
    
    if op == "=":
        if '[' in y:
            if '[' in y[2:-1]:
                y_idx = int(y[2:-1][2:-1])
                y_idx = eval(f"{y[2:-1][:2]}y_idx:y_idx+4]")
                y_idx = int.from_bytes(bytes(y_idx), 'little')
                exec(f"{x[:2]}x_idx:x_idx+4] = {y[:2]}y_idx:y_idx+4]")
            else:
                y_idx = eval(f"{y[2:-1]}")
                exec(f"{x[:2]}x_idx:x_idx+4] = {y[:2]}y_idx:y_idx+4]")
        else:
            exec(f"{x[:2]}x_idx:x_idx+4] = list(int.to_bytes(int(y), 4, 'little'))")
    
    else:
        xx = eval(f"{x[:2]}x_idx:x_idx+4]")

        xx = int.from_bytes(bytes(xx), 'little')

        if '[' in y:
            y_idx = eval(f"{y[2:-1]}")
            yy = eval(f"{y[:2]}y_idx:y_idx+4]")        
            yy = int.from_bytes(bytes(yy), 'little')
        else:
            yy = int(y)

        xx = eval(f"xx {op[:-1]} yy")
        xx &= 0xffffffff
        xx = list(int.to_bytes(xx, 4, 'little'))

        exec(f"{x[:2]}x_idx:x_idx+4] = xx")


def debug():
    _s = bytes(S[52:])
    idx = 52
    while _s:
        try:
            start = _s.index(b'%')
            end = start + _s[start:].index(b'\x00')
            print(f'==== S[{idx+start}] ====')
            print(_s[start:end].decode())
            parse(_s[start:end].decode(), True)
            print()
        
            idx += end
            _s = _s[end:]

        except:
            break

def dump():
    with open("s.dump", "wb") as f:
        f.write(bytes(S))

formats = parse(bytes(S[52:]).split(b'\x00')[0].decode(), False)
for z in formats:
    execute(*z.split())
==== S[52] ====
%0.4096hhM%0.255llI%1.0lM%1.8llL%0.1lU%1.0lM%1.16llL%0.1lU%1.200llM%2.1788llM%7C%-6144.1701736302llM%0.200hhM%0.255llI%0.37llO%0200.0C
D[0] = S[4096]
D[0] &= 255
D[4] = D[0]
D[4] <<= 8
D[0] |= D[4]
D[4] = D[0]
D[4] <<= 16
D[0] |= D[4]
D[4] = 200
D[8] = 1788
C S[7] 0 7
S[6144] = 1701736302
D[0] = S[200]
D[0] &= 255
D[0] -= 37
C S[200] 0 0

==== S[200] ====
%4.5000llM%0.13200llM%337C%0.0llM%500C%1262C%0653.0C
D[16] = 5000
D[0] = 13200
C S[337] 0 3
D[0] = 0
C S[500] 0 5
C S[1262] 0 1
C S[653] 0 0

==== S[253] ====
%1.0llM
D[4] = 0

==== S[261] ====
%3.0lM%3.2lN%0253.3C%2.1llS%3.2lM%3.3lX%3.0lO%3.1llO%-261.3C
D[12] = D[0]
D[12] %= D[8]
C S[253] 12 0
D[8] += 1
D[12] = D[8]
D[12] *= D[12]
D[12] -= D[0]
D[12] -= 1
C S[261] 12 -

==== S[322] ====
%+4.0lM%4.2llS
S[D[16]] = D[0]
D[16] += 2

==== S[337] ====
%1.1llM%2.2llM%261C%+322.1C%0.1llS%1.13600llM%1.0lO%+337.1C
D[4] = 1
D[8] = 2
C S[261] 0 2
C S[322] 4 +
D[0] += 1
D[4] = 13600
D[4] -= D[0]
C S[337] 4 +

==== S[397] ====
%0.0llM
D[0] = 0

==== S[405] ====
%0.2llV
D[0] //= 2

==== S[413] ====
%0.3llX%0.1llS
D[0] *= 3
D[0] += 1

==== S[428] ====
%1.0lM%1.2llN%0405.1C%+413.1C%470C%0.1llS
D[4] = D[0]
D[4] %= 2
C S[405] 4 0
C S[413] 4 +
C S[470] 0 4
D[0] += 1

==== S[470] ====
%1.0lM%1.1llO%0397.1C%+428.1C
D[4] = D[0]
D[4] -= 1
C S[397] 4 0
C S[428] 4 +

==== S[500] ====
%2.0lM%2.4096llS%4.2hM%4.255llI%+540.4C
D[8] = D[0]
D[8] += 4096
D[16] = S[D[8]]
D[16] &= 255
C S[540] 16 +

==== S[540] ====
%2.0lM%2.2llX%2.5000llS%2.2hM%2.255llI%4.2lE%0.1llS%2.0lM%470C%4.0lS%4.255llI%0.2lM%2.1llO%2.4500llS%+2.4lM%500C
D[8] = D[0]
D[8] *= 2
D[8] += 5000
D[8] = S[D[8]]
D[8] &= 255
D[16] ^= D[8]
D[0] += 1
D[8] = D[0]
C S[470] 0 4
D[16] += D[0]
D[16] &= 255
D[0] = D[8]
D[8] -= 1
D[8] += 4500
S[D[8]] = D[16]
C S[500] 0 5

==== S[653] ====
%0.123456789llM%1.0llM%1.4096llS%1.1hM%0.1lE%2.0llM%2.846786818llS%2.0lE%1.0llM%1.6144llS%+1.2lM%1.4llM%1.4096llS%1.1hM%0.1lE%2.0llM%2.1443538759llS%2.0lE%1.4llM%1.6144llS%+1.2lM%1.8llM%1.4096llS%1.1hM%0.1lE%2.0llM%2.1047515510llS%2.0lE%1.8llM%1.6144llS%+1.2lM%1.12llM%1.4096llS%1.1hM%0.1lE%2.0llM%2.359499514llS%2.1724461856llS%2.0lE%1.12llM%1.6144llS%+1.2lM%1.16llM%1.4096llS%1.1hM%0.1lE%2.0llM%2.241024035llS%2.0lE%1.16llM%1.6144llS%+1.2lM%1.20llM%1.4096llS%1.1hM%0.1lE%2.0llM%2.222267724llS%2.0lE%1.20llM%1.6144llS%+1.2lM%1.24llM%1.4096llS%1.1hM%0.1lE%2.0llM%2.844096018llS%2.0lE%1.24llM%1.6144llS%+1.2lM
D[0] = 123456789
D[4] = 0
D[4] += 4096
D[4] = S[D[4]]
D[0] ^= D[4]
D[8] = 0
D[8] += 846786818
D[8] ^= D[0]
D[4] = 0
D[4] += 6144
S[D[4]] = D[8]
D[4] = 4
D[4] += 4096
D[4] = S[D[4]]
D[0] ^= D[4]
D[8] = 0
D[8] += 1443538759
D[8] ^= D[0]
D[4] = 4
D[4] += 6144
S[D[4]] = D[8]
D[4] = 8
D[4] += 4096
D[4] = S[D[4]]
D[0] ^= D[4]
D[8] = 0
D[8] += 1047515510
D[8] ^= D[0]
D[4] = 8
D[4] += 6144
S[D[4]] = D[8]
D[4] = 12
D[4] += 4096
D[4] = S[D[4]]
D[0] ^= D[4]
D[8] = 0
D[8] += 359499514
D[8] += 1724461856
D[8] ^= D[0]
D[4] = 12
D[4] += 6144
S[D[4]] = D[8]
D[4] = 16
D[4] += 4096
D[4] = S[D[4]]
D[0] ^= D[4]
D[8] = 0
D[8] += 241024035
D[8] ^= D[0]
D[4] = 16
D[4] += 6144
S[D[4]] = D[8]
D[4] = 20
D[4] += 4096
D[4] = S[D[4]]
D[0] ^= D[4]
D[8] = 0
D[8] += 222267724
D[8] ^= D[0]
D[4] = 20
D[4] += 6144
S[D[4]] = D[8]
D[4] = 24
D[4] += 4096
D[4] = S[D[4]]
D[0] ^= D[4]
D[8] = 0
D[8] += 844096018
D[8] ^= D[0]
D[4] = 24
D[4] += 6144
S[D[4]] = D[8]

==== S[1262] ====
%0.0llM%1.0llM%1.4500llS%1.1hM%2.0llM%2.1374542625llS%2.1686915720llS%2.1129686860llS%1.2lE%0.1lU%1.4llM%1.4500llS%1.1hM%2.0llM%2.842217029llS%2.1483902564llS%1.2lE%0.1lU%1.8llM%1.4500llS%1.1hM%2.0llM%2.1868013731llS%1.2lE%0.1lU%1.12llM%1.4500llS%1.1hM%2.0llM%2.584694732llS%2.1453312700llS%1.2lE%0.1lU%1.16llM%1.4500llS%1.1hM%2.0llM%2.223548744llS%1.2lE%0.1lU%1.20llM%1.4500llS%1.1hM%2.0llM%2.1958883726llS%2.1916008099llS%1.2lE%0.1lU%1.24llM%1.4500llS%1.1hM%2.0llM%2.1829937605llS%2.1815356086llS%2.253836698llS%1.2lE%0.1lU
D[0] = 0
D[4] = 0
D[4] += 4500
D[4] = S[D[4]]
D[8] = 0
D[8] += 1374542625
D[8] += 1686915720
D[8] += 1129686860
D[4] ^= D[8]
D[0] |= D[4]
D[4] = 4
D[4] += 4500
D[4] = S[D[4]]
D[8] = 0
D[8] += 842217029
D[8] += 1483902564
D[4] ^= D[8]
D[0] |= D[4]
D[4] = 8
D[4] += 4500
D[4] = S[D[4]]
D[8] = 0
D[8] += 1868013731
D[4] ^= D[8]
D[0] |= D[4]
D[4] = 12
D[4] += 4500
D[4] = S[D[4]]
D[8] = 0
D[8] += 584694732
D[8] += 1453312700
D[4] ^= D[8]
D[0] |= D[4]
D[4] = 16
D[4] += 4500
D[4] = S[D[4]]
D[8] = 0
D[8] += 223548744
D[4] ^= D[8]
D[0] |= D[4]
D[4] = 20
D[4] += 4500
D[4] = S[D[4]]
D[8] = 0
D[8] += 1958883726
D[8] += 1916008099
D[4] ^= D[8]
D[0] |= D[4]
D[4] = 24
D[4] += 4500
D[4] = S[D[4]]
D[8] = 0
D[8] += 1829937605
D[8] += 1815356086
D[8] += 253836698
D[4] ^= D[8]
D[0] |= D[4]

겁나많네

코드도 생각좀 하고 짜야겠다

 

새로 옮겨쓴 코드

with open("s.dump", "rb") as f:
    S = list(f.read())

D = [0]*32
mask = (1 << 32) - 1

def S200():
    D[4] = 5000
    D[0] = 13200
    S337()
    D[0] = 0
    S500()
    S1262()
    if D[0] == 0:
        S653()

def S253():
    D[1] = 0

def S261():
    while True:
        D[3] = D[0]
        D[3] %= D[2]
        if D[3] == 0:
            S253()
        D[2] = (D[2] + 1) & mask
        D[3] = D[2]
        D[3] = (D[3] * D[3]) & mask 
        D[3] = (D[3] - D[0]) & mask
        D[3] = (D[3] - 1) & mask
        if D[3] < 0x80000000:
            break

def S322():
    S[D[4]:D[4]+4] = list(int.to_bytes(D[0], 4, 'little'))
    D[4] = (D[4] + 2) & mask

def S337():
    while True:
        D[1] = 1
        D[2] = 2
        S261()
        if 0 < D[1] < 0x80000000:
            S322()
        D[0] = (D[0] + 1) & mask
        D[1] = 13600
        D[1] = (D[1] - D[0]) & mask
        if D[1] >= 0x80000000 or D[1] == 0:
            break

def S397():
    D[0] = 0

def S405():
    D[0] //= 2

def S413():
    D[0] = (D[0] * 3 + 1) & mask

def S428():
    D[1] = D[0]
    D[1] %= 2
    if D[1] == 0:
        S405()
    if 0 < D[1] < 0x80000000:
        S413()
    S470()
    D[0] = (D[0] + 1) & mask

def S470():
    D[1] = (D[0] - 1) & mask
    if D[1] == 0:
        S397()
    if 0 < D[1] < 0x80000000:
        S428()

def S500():
    D[2] = D[0]
    D[2] = (D[0] + 4096) & mask
    D[4] = int.from_bytes(bytes(S[D[2]:D[2]+4]), 'little')
    D[4] &= 255
    if 0 < D[4] < 0x80000000:
        S540()

def S540():
    D[2] = D[0]
    D[2] = (D[2] * 2 + 5000) & mask
    D[2] = int.from_bytes(bytes(S[D[2]:D[2]+4]), 'little')
    D[2] &= 255
    D[4] ^= D[2]
    D[0] = (D[0] + 1) & mask
    D[2] = D[0]
    S470()
    D[4] = (D[4] + D[0]) & 255
    D[0] = D[2]
    D[2] = (D[2] - 1 + 4500) & mask
    S[D[2]:D[2]+4] = list(int.to_bytes(D[4], 4, 'little'))
    S500()

def S653():
    D[0] = 123456789
    
    D[1] = int.from_bytes(bytes(S[4096:4096+4]), 'little')
    D[0] ^= D[1]
    D[2] = 846786818
    D[2] ^= D[0]
    S[6144:6144+4] = list(int.to_bytes(D[2], 4, 'little'))

    D[1] = int.from_bytes(bytes(S[4096+4:4096+8]), 'little')
    D[0] ^= D[1]
    D[2] = 1443538759
    D[2] ^= D[0]
    S[6144+4:6144+8] = list(int.to_bytes(D[2], 4, 'little'))

    D[1] = int.from_bytes(bytes(S[4096+8:4096+12]), 'little')
    D[0] ^= D[1]
    D[2] = 1047515510 ^ D[0]
    S[6144+8:6144+12] = list(int.to_bytes(D[2], 4, 'little'))

    D[1] = int.from_bytes(bytes(S[4096+12:4096+16]), 'little')
    D[0] ^= D[1]
    D[2] = (359499514 + 1724461856) ^ D[0]
    S[6144+12:6144+16] = list(int.to_bytes(D[2], 4, 'little'))

    D[1] = int.from_bytes(bytes(S[4096+16:4096+20]), 'little')
    D[0] ^= D[1]
    D[2] = 241024035 ^ D[0]
    S[6144+16:6144+20] = list(int.to_bytes(D[2], 4, 'little'))

    D[1] = int.from_bytes(bytes(S[4096+20:4096+24]), 'little')
    D[0] ^= D[1]
    D[2] = 222267724 ^ D[0]
    S[6144+20:6144+24] = list(int.to_bytes(D[2], 4, 'little'))

    D[1] = int.from_bytes(bytes(S[4096+24:4096+28]), 'little') # 도시는 최대 28 바이트
    D[0] ^= D[1]
    D[2] = 844096018 ^ D[0]
    S[6144+24:6144+28] = list(int.to_bytes(D[2], 4, 'little'))

def S1262():
    D[0] = 0

    D[1] = int.from_bytes(bytes(S[4500:4500+4]), 'little')
    D[1] ^= (1374542625 + 1686915720 + 1129686860)
    D[0] |= D[1]

    D[1] = int.from_bytes(bytes(S[4500+4:4500+8]), 'little')
    D[1] ^= (842217029 + 1483902564)
    D[0] |= D[1]

    D[1] = int.from_bytes(bytes(S[4500+8:4500+12]), 'little')
    D[1] ^= 1868013731
    D[0] |= D[1]

    D[1] = int.from_bytes(bytes(S[4500+12:4500+16]), 'little')
    D[1] ^= (584694732 + 1453312700)
    D[0] |= D[1]

    D[1] = int.from_bytes(bytes(S[4500+16:4500+20]), 'little')
    D[1] ^= 223548744
    D[0] |= D[1]

    D[1] = int.from_bytes(bytes(S[4500+20:4500+24]), 'little')
    D[1] ^= (1958883726 + 1916008099)
    D[0] |= D[1]

    D[1] = int.from_bytes(bytes(S[4500+24:4500+28]), 'little')
    D[1] ^= (1829937605 + 1815356086 + 253836698)
    D[0] |= D[1]


city = 'T' + 'A'*27
S[4096:4096+28] = list(city.encode())

S200()

print(f"Flag : {bytes(S[6144:6144+28])}")

*(_DWORD *)&a52cS[6144] = 1701736302가 unk_6880에 none 넣는거였으니까 

 

6144 많이나오는 S[653]이 호출돼야 할 듯

-> S1262() 호출 이후 D[0] == 0이어야 함

 

S1262는 S[4500:4528]에 있는 값 이용

-> 4500을 찾았는데 S540에서 쓴다

 

여기저기 print 찍으면서 확인해봄

def S540():
    D[2] = D[0]
    D[2] = (D[2] * 2 + 5000) & mask
    D[2] = int.from_bytes(bytes(S[D[2]:D[2]+4]), 'little')
    D[2] &= 255
    # print(D[2], end=', ')
    # print(D[4])
    D[4] ^= D[2]                        # 입력값에 상수 xor
    D[0] = (D[0] + 1) & mask
    D[2] = D[0]
    S470()
    # print(D[0], end=', ')
    D[4] = (D[4] + D[0]) & 255          # 입력값에 상수 +
    D[0] = D[2]
    D[2] = (D[2] - 1 + 4500) & mask
    S[D[2]:D[2]+4] = list(int.to_bytes(D[4], 4, 'little'))  # 4500 ~ 4528에 D[4] 넣음
    S500()

이거면 풀 수 있을듯

 

from z3 import *

xor = [161, 163, 173, 185, 193, 203, 211, 235, 241, 253, 1, 15, 19, 25, 27, 55, 69, 85, 87, 99, 105, 109, 129, 139, 145, 151, 157, 165]
add = [0, 1, 7, 2, 5, 8, 16, 3, 19, 6, 14, 9, 9, 17, 17, 4, 12, 20, 20, 7, 7, 15, 15, 10, 23, 10, 111, 18]
res = [1374542625 + 1686915720 + 1129686860, 842217029 + 1483902564, 1868013731, 584694732 + 1453312700, 223548744, 1958883726 + 1916008099, 1829937605 + 1815356086 + 253836698]

s = Solver()
city = [BitVec(f'x{i}', 32) for i in range(28)]

s.add(city[0] == ord('T'))

go = [0]*28
for i in range(28):
    s.add(Or(And(0x20 <= city[i],city[i] <= 0x7f), city[i] == 0))
    go[i] = ((city[i] ^ xor[i]) + add[i]) & 0xff
    
x = [0]*7

for i in range(7):
    s.add(((go[i*4 + 3] << 24) | (go[i*4 + 2] << 16) | (go[i*4 + 1] << 8) | go[i*4]) == res[i])

while True:
    if s.check() != sat:
        break

    m = s.model()
    res = [int(m[i].as_long()) for i in city]

    print(''.join(map(chr, [res[i] for i in range(28)])))
    check = And([city[i] == res[i] for i in range(28)])
    s.add(check == False)

 

4바이트마다 xor되는 상수랑 비교해봤더니 도시처럼 보이는게 나옴

멍청했다 그냥 역연산 짤걸

xor = [161, 163, 173, 185, 193, 203, 211, 235, 241, 253, 1, 15, 19, 25, 27, 55, 69, 85, 87, 99, 105, 109, 129, 139, 145, 151, 157, 165]
add = [0, 1, 7, 2, 5, 8, 16, 3, 19, 6, 14, 9, 9, 17, 17, 4, 12, 20, 20, 7, 7, 15, 15, 10, 23, 10, 111, 18]
res = [1374542625 + 1686915720 + 1129686860, 842217029 + 1483902564, 1868013731, 584694732 + 1453312700, 223548744, 1958883726 + 1916008099, 1829937605 + 1815356086 + 253836698]

city = [ord('T')] + [0] *27

for idx in range(7):
    i = idx*4
    city[i] = ((res[idx] - add[i]) ^ xor[i]) & 0xff
    city[i+1] = (((res[idx] >> 8) - add[i+1]) ^ xor[i+1]) & 0xff
    city[i+2] = (((res[idx] >> 16) - add[i+2]) ^ xor[i+2]) & 0xff
    city[i+3] = (((res[idx] >> 24) - add[i+3]) ^ xor[i+3]) & 0xff

print(''.join(map(chr, city)))

 

 

 

https://lkmidas.github.io/posts/20210719-ggctf2021-writeups/

https://github.com/google/google-ctf/tree/master/2021/quals/rev-weather/challenge

문제 만드는거도 신기하다

'CTF > ㅁㄹ' 카테고리의 다른 글

[GoogleCTF quals 2021] adspam  (0) 2022.04.23