본문 바로가기
분류 전/CTF

[L3ak CTF 2025] babyrev 풀이

by jwcs 2025. 7. 16.
728x90
반응형

소문자 알파벳을 다른 문자로 치환하여 문자열이 암호화 되는데 이 과정을 역으로 수행하여 복호화하는 문제이다.

 

분석

.text:00000000000014AB                 lea     rax, input
.text:00000000000014B2                 mov     rdi, rax        ; s
.text:00000000000014B5                 call    _fgets
.text:00000000000014BA                 mov     [rbp+var_4], 0
.text:00000000000014C1                 jmp     short loc_1502

입력 값을 받고 있다. 그리고 [rbp+var_4]를 0으로 초기화하고 short_loc_1502로 점프하고 있다.

 

.text:0000000000001502 loc_1502:                               ; CODE XREF: main+83↑j
.text:0000000000001502                 mov     eax, [rbp+var_4]
.text:0000000000001505                 cdqe
.text:0000000000001507                 lea     rdx, input
.text:000000000000150E                 movzx   eax, byte ptr [rax+rdx]
.text:0000000000001512                 test    al, al
.text:0000000000001514                 jnz     short loc_14C3

앞서 초기화한 [rbp+var_4]를 eax에 담고 있다.  그리고 input의 주소를 rdx에 담고 있다. [rbp+var_4]는 반복문의 i와 같은 역할을 하고 있는 것으로 보이고, ptr [rax + rdx]를 해석해보면 input[i]이다. test al, al과 jnz는 input[i]가 0인지 검사하는 것이다.

 

test는 and 연산을 통해 값이 0이면 zero flag = 1, 값이 음수면 sign flag = 1을 수행한다. cmp와는 달리 값을 저장하지는 않는다.

 

.text:00000000000014C3 loc_14C3:                               ; CODE XREF: main+D6↓j
.text:00000000000014C3                 mov     eax, [rbp+var_4]
.text:00000000000014C6                 cdqe
.text:00000000000014C8                 lea     rdx, input
.text:00000000000014CF                 movzx   eax, byte ptr [rax+rdx]
.text:00000000000014D3                 mov     [rbp+var_5], al
.text:00000000000014D6                 movzx   eax, [rbp+var_5]
.text:00000000000014DA                 test    al, al
.text:00000000000014DC                 js      short loc_14FE

.text:00000000000014FE loc_14FE:                               ; CODE XREF: main+9E↑j
.text:00000000000014FE                 add     [rbp+var_4], 1

input[i]가 음수이면 스킵한다. 이 말을 해석해보자. 우리는 문자열을 입력했다. 그럼 0 ~ 127까지의 아스키 테이블의 값일 것이다. 근데 만약 음수라고 하면 아스키 테이블을 벗어나는 값이다. 이런 값들은 스킵한다는 의미이다. init_remap을 통해서 아스키 테이블 크기만큼 remap을 초기화하기 때문으로 생각된다.

 

.text:00000000000014DE                 movzx   eax, [rbp+var_5]
.text:00000000000014E2                 cdqe
.text:00000000000014E4                 lea     rdx, remap
.text:00000000000014EB                 movzx   edx, byte ptr [rax+rdx]
.text:00000000000014EF                 mov     eax, [rbp+var_4]
.text:00000000000014F2                 cdqe
.text:00000000000014F4                 lea     rcx, input
.text:00000000000014FB                 mov     [rax+rcx], dl
.text:00000000000014FE
.text:00000000000014FE loc_14FE:                               ; CODE XREF: main+9E↑j
.text:00000000000014FE                 add     [rbp+var_4], 1

해석해보면 input[i] = remap[input[i]]으로 요약할 수 있다.

 

.text:0000000000001502 loc_1502:                               ; CODE XREF: main+83↑j
.text:0000000000001502                 mov     eax, [rbp+var_4]
.text:0000000000001505                 cdqe
.text:0000000000001507                 lea     rdx, input
.text:000000000000150E                 movzx   eax, byte ptr [rax+rdx]
.text:0000000000001512                 test    al, al
.text:0000000000001514                 jnz     short loc_14C3
.text:0000000000001516                 lea     rax, flag       ; "L3AK{ngx_qkt_fgz_ugffq_uxtll_dt}"
.text:000000000000151D                 mov     rdi, rax        ; s
.text:0000000000001520                 call    _strlen
.text:0000000000001525                 mov     rdx, rax        ; n
.text:0000000000001528                 lea     rax, flag       ; "L3AK{ngx_qkt_fgz_ugffq_uxtll_dt}"
.text:000000000000152F                 mov     rsi, rax        ; s2
.text:0000000000001532                 lea     rax, input
.text:0000000000001539                 mov     rdi, rax        ; s1
.text:000000000000153C                 call    _strncmp
.text:0000000000001541                 test    eax, eax
.text:0000000000001543                 jnz     short loc_1556
.text:0000000000001545                 lea     rax, s          ; "Correct! Here is your prize."

위에서 remap[input[i]]한 값과 flag를 비교하여 동일한지 검사하고 있다. 여기서 암호화된 flag를 알 수 있으므로, 역으로 계산하면 우리가 무슨 값을 입력해야 하는지 알 수 있다.

 

.text:000000000000128D                 push    rbp
.text:000000000000128E                 mov     rbp, rsp
.text:0000000000001291                 mov     [rbp+var_4], 0
.text:0000000000001298                 jmp     short loc_12B2
.text:000000000000129A ; ---------------------------------------------------------------------------
.text:000000000000129A
.text:000000000000129A loc_129A:                               ; CODE XREF: init_remap+2D↓j
.text:000000000000129A                 mov     eax, [rbp+var_4]
.text:000000000000129D                 mov     ecx, eax
.text:000000000000129F                 mov     eax, [rbp+var_4]
.text:00000000000012A2                 cdqe
.text:00000000000012A4                 lea     rdx, remap
.text:00000000000012AB                 mov     [rax+rdx], cl
.text:00000000000012AE                 add     [rbp+var_4], 1
.text:00000000000012B2
.text:00000000000012B2 loc_12B2:                               ; CODE XREF: init_remap+F↑j
.text:00000000000012B2                 cmp     [rbp+var_4], 7Fh
.text:00000000000012B6                 jle     short loc_129A
.text:00000000000012B8                 mov     cs:byte_4121, 71h ; 'q'
.text:00000000000012BF                 mov     cs:byte_4122, 77h ; 'w'
.text:00000000000012C6                 mov     cs:byte_4123, 65h ; 'e'
.text:00000000000012CD                 mov     cs:byte_4124, 72h ; 'r'
.text:00000000000012D4                 mov     cs:byte_4125, 74h ; 't'
.text:00000000000012DB                 mov     cs:byte_4126, 79h ; 'y'
.text:00000000000012E2                 mov     cs:byte_4127, 75h ; 'u'
.text:00000000000012E9                 mov     cs:byte_4128, 69h ; 'i'
.text:00000000000012F0                 mov     cs:byte_4129, 6Fh ; 'o'
.text:00000000000012F7                 mov     cs:byte_412A, 70h ; 'p'
.text:00000000000012FE                 mov     cs:byte_412B, 61h ; 'a'
.text:0000000000001305                 mov     cs:byte_412C, 73h ; 's'
.text:000000000000130C                 mov     cs:byte_412D, 64h ; 'd'
.text:0000000000001313                 mov     cs:byte_412E, 66h ; 'f'
.text:000000000000131A                 mov     cs:byte_412F, 67h ; 'g'
.text:0000000000001321                 mov     cs:byte_4130, 68h ; 'h'
.text:0000000000001328                 mov     cs:byte_4131, 6Ah ; 'j'
.text:000000000000132F                 mov     cs:byte_4132, 6Bh ; 'k'
.text:0000000000001336                 mov     cs:byte_4133, 6Ch ; 'l'
.text:000000000000133D                 mov     cs:byte_4134, 7Ah ; 'z'
.text:0000000000001344                 mov     cs:byte_4135, 78h ; 'x'
.text:000000000000134B                 mov     cs:byte_4136, 63h ; 'c'
.text:0000000000001352                 mov     cs:byte_4137, 76h ; 'v'
.text:0000000000001359                 mov     cs:byte_4138, 62h ; 'b'
.text:0000000000001360                 mov     cs:byte_4139, 6Eh ; 'n'
.text:0000000000001367                 mov     cs:byte_413A, 6Dh ; 'm'
.text:000000000000136E                 nop
.text:000000000000136F                 pop     rbp
.text:0000000000001370                 retn
.text:0000000000001370 ; } // starts at 1289
.text:0000000000001370 init_remap      endp

remap의 시작 주소를 가져와서 총 0x7F만큼 각 인덱스의 값으로 초기화하고 있다. 그 이후 byte_4121부터 커스텀 테이블로 값을 덮어씌우고 있다.

 

remap 주소

byte_4121의 주소는 4121이고, remap의 시작 주소는 40c0이다. 이를 빼면 십진수로 97이 나오게 된다.

 

아스키 코드 테이블 표

이는 소문자 a에 해당한다. 다시 말해서, 다른 값들은 각 인덱스 그대로 값을 할당하지만 소문자는 커스텀한 값들로 치환한다는 것을 알 수 있다.

 

익스플로잇

byte_4121=list("qwertyuiopasdfghjklzxcvbnm")
flag = list("L3AK{ngx_qkt_fgz_ugffq_uxtll_dt}")
str=[]

j=0
for i in range(0x7f):
    if i >= 0x61 and i < 0x61 + 26:
        str.append(byte_4121[j])
        j += 1
    else:
        str.append(chr(i))

print(f"table: {str}")
print(f"enc: {''.join(flag)}")

dec_byte_4121 = []
for i in flag:
    if i in byte_4121:
        idx = byte_4121.index(i)
        dec_byte_4121.append(chr(ord('a') + idx))
    else:
        dec_byte_4121.append(i)
print(f"dec: {''.join(dec_byte_4121)}")

필자는 위와 같은 코드로 flag를 구했다. 

 

문자열의 각 값을 테이블의 몇 번째 인덱스에 있는지 구하여 97을 더하면 원래 값을 알 수 있다.

예를 들어 'q'는 테이블에서 인덱스가 0이다. ord('a') + 0을 하게되면 원래 문자인 a를 구할 수 있다.

 

output

L3AK{you_are_not_gonna_guess_me}

짜잔

728x90
반응형

'분류 전 > CTF' 카테고리의 다른 글

[DownUnderCTF 2025] rocky 풀이  (0) 2025.07.22
[DownUnderCTF 2025] mini-me 풀이  (0) 2025.07.22
[L3ak CTF 2025] BrainCalc 풀이  (0) 2025.07.15
[L3ak CTF 2025] Flag L3ak 풀이  (0) 2025.07.15
[R3CTF 2025] web-evalgelist 풀이  (3) 2025.07.13