LG Uplus 2024

» CTF

image.png

8등 roKyC의 팀원

리버싱 3문제 second, second, first blood.

CrackME

golang으로 작성된 바이너리입니다.

image.png

input을 입력 받은 뒤 특정 연산 후 결과를 검증합니다.

이 때 이 특정 연산은 행렬곱과 완전히 동일하며, 위 사진에서 1172번째 line의 반복문입니다.

input 행렬과 특정 행렬을 곱하게 되며, 그 결과로 나온 벡터를 검증합니다.

이 때 연산에 사용되는 행렬에 해당하는 값을 초기화 하는 과정이 난잡해 보이지만, 디버깅 수행하여 값을 뽑아 주었습니다.

아래는 디버깅해서 연산에 사용되는 행렬을 뽑아오는 gdb script입니다.

# gdb -q -x gdbscript.py
import gdb

ge = gdb.execute
gp = gdb.parse_and_eval

ge("file ./prob")

bp = 0x49F016

gdb.Breakpoint(f"*{bp}")

input = "B" * 32
ge("run <<< " + input)
Mat = []
for i in range(32):
    tmp = []
    for j in range(32):
        r8 = int(gp("$r8"))
        tmp.append(r8)
        ge("c", to_string=True)
    Mat.append(tmp)
    print(tmp)

print(Mat)
exit()

아래는 솔버 코드입니다.

A = [[236, 208, 121, 66, 248, 31, 48, 181, 32, 255, 45, 27, 226, 176, 9, 62, 61, 118, 241, 165, 169, 22, 31, 91, 207, 8, 15, 137, 130, 187, 4, 180], [144, 57, 75, 37, 56, 115, 171, 119, 168, 106, 95, 252, 194, 173, 79, 105, 5, 224, 17, 215, 66, 242, 221, 115, 61, 221, 185, 181, 73, 131, 149, 124], [190, 2, 115, 93, 103, 66, 59, 44, 26, 10, 115, 197, 7, 232, 138, 33, 218, 5, 112, 199, 104, 208, 210, 74, 160, 191, 207, 14, 105, 50, 51, 231], [161, 31, 189, 199, 69, 16, 139, 210, 3, 67, 225, 135, 220, 68, 22, 162, 151, 39, 236, 137, 209, 2, 113, 219, 150, 74, 50, 241, 202, 109, 253, 42], [245, 245, 230, 1, 177, 45, 129, 131, 150, 138, 191, 42, 252, 41, 157, 66, 155, 156, 175, 204, 104, 134, 147, 134, 115, 140, 120, 17, 147, 225, 11, 71], [86, 199, 236, 138, 92, 166, 214, 186, 5, 0, 207, 136, 24, 0, 128, 77, 175, 242, 133, 150, 43, 85, 17, 3, 157, 52, 44, 253, 220, 221, 188, 156], [91, 7, 248, 86, 102, 7, 204, 179, 23, 170, 181, 6, 254, 35, 36, 13, 36, 240, 11, 45, 20, 120, 115, 58, 102, 46, 210, 187, 218, 138, 52, 54], [68, 46, 17, 247, 244, 162, 142, 198, 141, 88, 113, 132, 119, 64, 60, 30, 96, 157, 117, 254, 235, 49, 39, 235, 68, 27, 90, 167, 42, 154, 10, 65], [25, 43, 109, 39, 159, 69, 185, 165, 173, 88, 88, 210, 150, 200, 43, 242, 124, 9, 2, 151, 83, 119, 250, 232, 15, 92, 89, 250, 67, 230, 249, 71], [240, 71, 161, 126, 69, 200, 141, 53, 48, 91, 236, 80, 167, 111, 133, 227, 97, 100, 159, 255, 65, 65, 81, 91, 119, 1, 93, 220, 175, 29, 24, 101], [242, 183, 175, 194, 98, 92, 207, 66, 144, 66, 33, 42, 62, 246, 8, 184, 193, 135, 15, 6, 153, 114, 232, 18, 166, 99, 125, 121, 100, 230, 17, 48], [86, 41, 5, 190, 175, 38, 176, 33, 153, 188, 241, 171, 38, 116, 70, 59, 55, 72, 240, 33, 92, 24, 61, 193, 39, 162, 252, 133, 255, 227, 242, 24], [180, 104, 107, 135, 174, 96, 38, 65, 38, 180, 110, 33, 220, 172, 212, 108, 208, 130, 138, 211, 98, 110, 41, 243, 4, 127, 76, 113, 57, 19, 206, 89], [137, 186, 61, 4, 179, 64, 9, 51, 61, 98, 97, 185, 135, 116, 63, 118, 194, 41, 104, 198, 46, 216, 20, 0, 187, 8, 224, 33, 229, 133, 211, 197], [72, 129, 124, 32, 254, 123, 51, 149, 64, 117, 106, 238, 154, 159, 170, 101, 211, 81, 181, 9, 191, 214, 24, 106, 200, 72, 165, 20, 113, 12, 235, 220], [157, 72, 157, 14, 47, 231, 24, 29, 79, 166, 168, 45, 237, 150, 81, 45, 74, 18, 64, 152, 108, 134, 117, 213, 218, 106, 251, 125, 227, 170, 93, 2], [48, 98, 231, 129, 199, 53, 139, 159, 105, 1, 227, 33, 61, 178, 97, 136, 41, 237, 172, 160, 129, 140, 63, 163, 63, 77, 9, 79, 215, 102, 132, 197], [245, 139, 3, 147, 36, 118, 138, 217, 32, 189, 207, 230, 236, 24, 138, 97, 6, 161, 56, 247, 81, 36, 67, 151, 214, 210, 172, 16, 110, 64, 255, 2], [176, 208, 120, 148, 222, 0, 184, 139, 102, 71, 211, 45, 249, 3, 112, 80, 89, 230, 2, 150, 238, 140, 121, 151, 222, 107, 215, 66, 190, 122, 94, 53], [161, 179, 252, 161, 9, 52, 4, 136, 73, 242, 18, 167, 24, 146, 83, 96, 228, 23, 68, 169, 141, 50, 197, 53, 167, 51, 251, 110, 4, 198, 30, 173], [48, 144, 190, 223, 21, 107, 38, 15, 131, 182, 138, 98, 184, 58, 2, 217, 215, 201, 123, 38, 102, 46, 111, 191, 18, 255, 71, 137, 138, 120, 195, 221], [31, 104, 184, 6, 31, 41, 181, 193, 1, 29, 171, 249, 247, 47, 23, 64, 134, 10, 207, 10, 190, 167, 170, 39, 174, 154, 181, 67, 42, 199, 167, 24], [33, 120, 51, 166, 107, 45, 47, 174, 176, 114, 98, 107, 222, 66, 226, 121, 229, 234, 47, 15, 3, 84, 75, 217, 243, 233, 189, 73, 238, 32, 53, 196], [55, 37, 193, 189, 85, 49, 85, 58, 182, 169, 110, 92, 171, 151, 104, 216, 152, 43, 209, 162, 36, 81, 213, 168, 0, 225, 98, 155, 250, 42, 108, 133], [69, 234, 101, 217, 71, 58, 4, 249, 163, 96, 221, 132, 40, 193, 64, 254, 222, 35, 139, 197, 16, 101, 46, 92, 179, 200, 242, 140, 24, 195, 112, 70], [237, 172, 186, 247, 35, 6, 254, 197, 46, 229, 252, 32, 239, 217, 136, 11, 63, 66, 223, 81, 34, 223, 140, 207, 201, 221, 110, 247, 129, 252, 143, 87], [172, 102, 73, 42, 72, 46, 171, 19, 9, 188, 121, 241, 76, 243, 10, 197, 227, 176, 93, 239, 251, 54, 193, 5, 222, 31, 49, 135, 232, 211, 119, 109], [184, 238, 66, 59, 112, 67, 37, 36, 118, 221, 141, 53, 252, 105, 8, 52, 52, 34, 139, 121, 105, 120, 184, 77, 42, 218, 236, 31, 224, 47, 122, 206], [183, 72, 77, 226, 94, 250, 60, 220, 46, 229, 46, 64, 10, 151, 182, 96, 181, 114, 64, 172, 83, 118, 212, 251, 25, 51, 92, 209, 254, 79, 112, 59], [24, 122, 101, 253, 68, 35, 59, 76, 108, 235, 70, 237, 132, 65, 123, 72, 113, 193, 232, 149, 128, 194, 57, 55, 99, 175, 58, 133, 25, 204, 244, 5], [38, 42, 61, 165, 31, 159, 63, 65, 30, 56, 22, 17, 113, 212, 143, 79, 8, 85, 179, 208, 10, 156, 243, 41, 163, 215, 190, 220, 172, 172, 177, 135], [48, 110, 177, 218, 210, 141, 28, 183, 182, 76, 179, 250, 79, 186, 73, 218, 113, 214, 18, 212, 65, 166, 130, 202, 97, 175, 179, 202, 151, 230, 233, 19]]

cmp_table = [
    404837, 433880, 375060, 440450, 452809, 424432, 346432, 384447,
    422513, 402020, 399972, 409559, 405811, 388584, 439638, 399067,
    408350, 425210, 448994, 391594, 421764, 375389, 426642, 422203,
    440248, 519052, 442455, 397126, 426532, 403109, 382633, 497133
]

from sage.all import *

MatA = Matrix(A)
MatB = vector(cmp_table)

flag = MatA.solve_right(MatB)

print(flag)

flag = [103, 111, 95, 97, 110, 100, 95, 109, 97, 116, 114, 105, 120, 115, 95, 105, 115, 95, 104, 97, 114, 100, 95, 97, 110, 100, 95, 102, 117, 110, 110, 121]

print(bytes(flag))

image.png

flag: lguplus2024{go_and_matrixs_is_hard_and_funny}


VMVMVM

매우 전형적인 VM 문제입니다.

문제 바이너리의 VM 명령어 셋을 분석한 뒤, command로 사용되는 값들을 명령어 셋 포멧으로 해석해주면 됩니다.

cmd = [7, 0, 7, 64, 15, 7, 0, 7, 65, 15, 7, 64, 14, 7, 128, 12, 7, 64, 14, 7, 0, 1, 14, 11, 7, 64, 14, 7, 0, 1, 15, 7, 1, 7, 64, 14, 7, 0, 1, 14, 13, 7, 64, 14, 7, 32, 1, 14, 2, 7, 61, 9, 7, 65, 14, 7, 1, 1, 7, 65, 15, 7, 64, 14, 7, 1, 1, 7, 64, 15, 7, 64, 14, 7, 32, 2, 7, 10, 10, 7, 65, 14, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

def parse(cmd):
    output = ""
    pc = 0
    ret_flag = False
    while True:
        if ret_flag:
            break
        tmp_output = f"{pc}"
        tmp_output.ljust(3, " ")
        tmp_output += ": "
        opcode = cmd[pc]
        pc += 1

        if opcode == 1:
            tmp_output += "push (pop + pop)"
        elif opcode == 2:
            tmp_output += "push (pop - pop)"
        elif opcode == 3:
            tmp_output += "push (pop * pop)"
        elif opcode == 4:
            tmp_output += "push (pop / pop)"
        elif opcode == 5:
            tmp_output += "push (pop % pop)"
        elif opcode == 6:
            tmp_output += "return pop"
            ret_flag = True
        elif opcode == 7:
            data = cmd[pc]
            pc += 1
            tmp_output += f"push {data}"
        elif opcode == 8:
            tmp_output += "pop"
        elif opcode == 9:
            tmp_output += "je pop(newpc) pop(test)"
        elif opcode == 10:
            tmp_output += "jne pop(newpc) pop(test)"
        elif opcode == 11:
            tmp_output += "push (pop ^ pop)"
        elif opcode == 12:
            tmp_output += "push ror(pop, pop)"
        elif opcode == 13:
            tmp_output += "push rol(pop, pop)"
        elif opcode == 14:
            tmp_output += "push data[pop]"
        elif opcode == 15:
            tmp_output += "data[pop] = pop"
        
        output += tmp_output + "\n"
    
    return output

print(parse(cmd))

실행하면 다음과 같은 해석 결과가 나오게 됩니다.

0: push 0
2: push 64
4: data[pop] = pop
5: push 0
7: push 65
9: data[pop] = pop
10: push 64
12: push data[pop]
13: push 128
15: push ror(pop, pop)
16: push 64
18: push data[pop]
19: push 0
21: push (pop + pop)
22: push data[pop]
23: push (pop ^ pop)
24: push 64
26: push data[pop]
27: push 0
29: push (pop + pop)
30: data[pop] = pop
31: push 1
33: push 64
35: push data[pop]
36: push 0
38: push (pop + pop)
39: push data[pop]
40: push rol(pop, pop)
41: push 64
43: push data[pop]
44: push 32
46: push (pop + pop)
47: push data[pop]
48: push (pop - pop)
49: push 61
51: je pop(newpc) pop(test)
52: push 65
54: push data[pop]
55: push 1
57: push (pop + pop)
58: push 65
60: data[pop] = pop
61: push 64
63: push data[pop]
64: push 1
66: push (pop + pop)
67: push 64
69: data[pop] = pop
70: push 64
72: push data[pop]
73: push 32
75: push (pop - pop)
76: push 10
78: jne pop(newpc) pop(test)
79: push 65
81: push data[pop]
82: return pop

짧고 간단하기 때문에 위 코드를 해석한 뒤 역산 해주면 됩니다.

A = [[236, 208, 121, 66, 248, 31, 48, 181, 32, 255, 45, 27, 226, 176, 9, 62, 61, 118, 241, 165, 169, 22, 31, 91, 207, 8, 15, 137, 130, 187, 4, 180], [144, 57, 75, 37, 56, 115, 171, 119, 168, 106, 95, 252, 194, 173, 79, 105, 5, 224, 17, 215, 66, 242, 221, 115, 61, 221, 185, 181, 73, 131, 149, 124], [190, 2, 115, 93, 103, 66, 59, 44, 26, 10, 115, 197, 7, 232, 138, 33, 218, 5, 112, 199, 104, 208, 210, 74, 160, 191, 207, 14, 105, 50, 51, 231], [161, 31, 189, 199, 69, 16, 139, 210, 3, 67, 225, 135, 220, 68, 22, 162, 151, 39, 236, 137, 209, 2, 113, 219, 150, 74, 50, 241, 202, 109, 253, 42], [245, 245, 230, 1, 177, 45, 129, 131, 150, 138, 191, 42, 252, 41, 157, 66, 155, 156, 175, 204, 104, 134, 147, 134, 115, 140, 120, 17, 147, 225, 11, 71], [86, 199, 236, 138, 92, 166, 214, 186, 5, 0, 207, 136, 24, 0, 128, 77, 175, 242, 133, 150, 43, 85, 17, 3, 157, 52, 44, 253, 220, 221, 188, 156], [91, 7, 248, 86, 102, 7, 204, 179, 23, 170, 181, 6, 254, 35, 36, 13, 36, 240, 11, 45, 20, 120, 115, 58, 102, 46, 210, 187, 218, 138, 52, 54], [68, 46, 17, 247, 244, 162, 142, 198, 141, 88, 113, 132, 119, 64, 60, 30, 96, 157, 117, 254, 235, 49, 39, 235, 68, 27, 90, 167, 42, 154, 10, 65], [25, 43, 109, 39, 159, 69, 185, 165, 173, 88, 88, 210, 150, 200, 43, 242, 124, 9, 2, 151, 83, 119, 250, 232, 15, 92, 89, 250, 67, 230, 249, 71], [240, 71, 161, 126, 69, 200, 141, 53, 48, 91, 236, 80, 167, 111, 133, 227, 97, 100, 159, 255, 65, 65, 81, 91, 119, 1, 93, 220, 175, 29, 24, 101], [242, 183, 175, 194, 98, 92, 207, 66, 144, 66, 33, 42, 62, 246, 8, 184, 193, 135, 15, 6, 153, 114, 232, 18, 166, 99, 125, 121, 100, 230, 17, 48], [86, 41, 5, 190, 175, 38, 176, 33, 153, 188, 241, 171, 38, 116, 70, 59, 55, 72, 240, 33, 92, 24, 61, 193, 39, 162, 252, 133, 255, 227, 242, 24], [180, 104, 107, 135, 174, 96, 38, 65, 38, 180, 110, 33, 220, 172, 212, 108, 208, 130, 138, 211, 98, 110, 41, 243, 4, 127, 76, 113, 57, 19, 206, 89], [137, 186, 61, 4, 179, 64, 9, 51, 61, 98, 97, 185, 135, 116, 63, 118, 194, 41, 104, 198, 46, 216, 20, 0, 187, 8, 224, 33, 229, 133, 211, 197], [72, 129, 124, 32, 254, 123, 51, 149, 64, 117, 106, 238, 154, 159, 170, 101, 211, 81, 181, 9, 191, 214, 24, 106, 200, 72, 165, 20, 113, 12, 235, 220], [157, 72, 157, 14, 47, 231, 24, 29, 79, 166, 168, 45, 237, 150, 81, 45, 74, 18, 64, 152, 108, 134, 117, 213, 218, 106, 251, 125, 227, 170, 93, 2], [48, 98, 231, 129, 199, 53, 139, 159, 105, 1, 227, 33, 61, 178, 97, 136, 41, 237, 172, 160, 129, 140, 63, 163, 63, 77, 9, 79, 215, 102, 132, 197], [245, 139, 3, 147, 36, 118, 138, 217, 32, 189, 207, 230, 236, 24, 138, 97, 6, 161, 56, 247, 81, 36, 67, 151, 214, 210, 172, 16, 110, 64, 255, 2], [176, 208, 120, 148, 222, 0, 184, 139, 102, 71, 211, 45, 249, 3, 112, 80, 89, 230, 2, 150, 238, 140, 121, 151, 222, 107, 215, 66, 190, 122, 94, 53], [161, 179, 252, 161, 9, 52, 4, 136, 73, 242, 18, 167, 24, 146, 83, 96, 228, 23, 68, 169, 141, 50, 197, 53, 167, 51, 251, 110, 4, 198, 30, 173], [48, 144, 190, 223, 21, 107, 38, 15, 131, 182, 138, 98, 184, 58, 2, 217, 215, 201, 123, 38, 102, 46, 111, 191, 18, 255, 71, 137, 138, 120, 195, 221], [31, 104, 184, 6, 31, 41, 181, 193, 1, 29, 171, 249, 247, 47, 23, 64, 134, 10, 207, 10, 190, 167, 170, 39, 174, 154, 181, 67, 42, 199, 167, 24], [33, 120, 51, 166, 107, 45, 47, 174, 176, 114, 98, 107, 222, 66, 226, 121, 229, 234, 47, 15, 3, 84, 75, 217, 243, 233, 189, 73, 238, 32, 53, 196], [55, 37, 193, 189, 85, 49, 85, 58, 182, 169, 110, 92, 171, 151, 104, 216, 152, 43, 209, 162, 36, 81, 213, 168, 0, 225, 98, 155, 250, 42, 108, 133], [69, 234, 101, 217, 71, 58, 4, 249, 163, 96, 221, 132, 40, 193, 64, 254, 222, 35, 139, 197, 16, 101, 46, 92, 179, 200, 242, 140, 24, 195, 112, 70], [237, 172, 186, 247, 35, 6, 254, 197, 46, 229, 252, 32, 239, 217, 136, 11, 63, 66, 223, 81, 34, 223, 140, 207, 201, 221, 110, 247, 129, 252, 143, 87], [172, 102, 73, 42, 72, 46, 171, 19, 9, 188, 121, 241, 76, 243, 10, 197, 227, 176, 93, 239, 251, 54, 193, 5, 222, 31, 49, 135, 232, 211, 119, 109], [184, 238, 66, 59, 112, 67, 37, 36, 118, 221, 141, 53, 252, 105, 8, 52, 52, 34, 139, 121, 105, 120, 184, 77, 42, 218, 236, 31, 224, 47, 122, 206], [183, 72, 77, 226, 94, 250, 60, 220, 46, 229, 46, 64, 10, 151, 182, 96, 181, 114, 64, 172, 83, 118, 212, 251, 25, 51, 92, 209, 254, 79, 112, 59], [24, 122, 101, 253, 68, 35, 59, 76, 108, 235, 70, 237, 132, 65, 123, 72, 113, 193, 232, 149, 128, 194, 57, 55, 99, 175, 58, 133, 25, 204, 244, 5], [38, 42, 61, 165, 31, 159, 63, 65, 30, 56, 22, 17, 113, 212, 143, 79, 8, 85, 179, 208, 10, 156, 243, 41, 163, 215, 190, 220, 172, 172, 177, 135], [48, 110, 177, 218, 210, 141, 28, 183, 182, 76, 179, 250, 79, 186, 73, 218, 113, 214, 18, 212, 65, 166, 130, 202, 97, 175, 179, 202, 151, 230, 233, 19]]

cmp_table = [
    404837, 433880, 375060, 440450, 452809, 424432, 346432, 384447,
    422513, 402020, 399972, 409559, 405811, 388584, 439638, 399067,
    408350, 425210, 448994, 391594, 421764, 375389, 426642, 422203,
    440248, 519052, 442455, 397126, 426532, 403109, 382633, 497133
]

from sage.all import *

MatA = Matrix(A)
MatB = vector(cmp_table)

flag = MatA.solve_right(MatB)

print(flag)

flag = [103, 111, 95, 97, 110, 100, 95, 109, 97, 116, 114, 105, 120, 115, 95, 105, 115, 95, 104, 97, 114, 100, 95, 97, 110, 100, 95, 102, 117, 110, 110, 121]

print(bytes(flag))

image.png

flag: lguplus2024{1309fda9bec915a45b5f5be23928ae26}


Encryptor

Encrypt하는 알고리즘은 간단하기 때문에, 자세한 설명은 생략하겠습니다.

간단하게 얘기하면, 7바이트 단위로 block을 나누고 rotate shift 같은 연산 수행 후에, 7바이트에 대한 crc 값을 계산하여 7바이트 뒤에 붙여서 8바이트로 만듭니다.

그리고 urandom에서 랜덤 값 받아와서 8바이트 중 랜덤 비트 하나를 flip 시킵니다.

rotate shift 유사한 연산에서도 srand(time(0))으로 설정된 rand() 반환 값이 사용됩니다.

여기서 평문을 복원해내기 위해서는 urandom값과 rand()값을 알아 내야 합니다.

urandom값은 각 블럭의 CRC 값을 이용하면

8 * 8번의 brute-force를 통해 알아낼 수 있습니다.

그리고, rand() 값은 brute-force가 가능한 범위가 아니기에 seed 값을 예측하는 쪽으로 접근해 보면 파일 생성 시간을 얻어낸다면 그 시간이 srand의 시간일 확률이 매우 높습니다.

image.png

2024년 11월 5일 20시 31분 1초 (KST)를 UNIX 타임으로 변환하여 seed값으로 넣어준 뒤 Decrypt를 시도해 봤습니다.

image.png

아래는 1730806261을 시드로 해서 Decrypt하는 솔버 코드입니다.

from ctypes import CDLL

libc = CDLL("libc.so.6")
libc.srand(1730806261)

def crc_calc(val:list):
    res = 0
    for i in range(7):
        res ^= val[i]
        for j in range(8):
            if res < 0x80:
                res *= 2
            else:
                res = (2 * res) ^ 7
            res &= 0xff
    
    return res

enc = open("flag.png.enc", "rb").read()

assert len(enc) % 8 == 0

checked = []
for i in range(len(enc) // 8):
    for rand1 in range(8):
        for rand2 in range(8):
            block = list(enc[8*i:8*(i+1)])
            block[rand1] ^= 1 << rand2
            if crc_calc(block[:7]) == block[7]:
                checked.append(block[:7])

dec = []
for block in checked:
    sh = libc.rand() % 7

    bit = []
    for val in block:
        for bit_num in range(8):
            bit.append((val >> bit_num) & 1)
    
    for i in reversed(range(56)):
        if (i + sh) <= 55:
            tmp = bit[i]
            bit[i] = bit[i+sh]
            bit[i+sh] = tmp
    
    dec_block = []
    for i in range(7):
        res = 0
        for j in range(8):
            res |= (bit[8*i + j] << j)
        dec_block.append(res)
    
    for val in dec_block:
        dec.append(val)

open("flag.png", "wb").write(bytes(dec))

아래와 같이 PNG 파일을 얻을 수 있습니다.

image.png

flag: lguplus2024{7d983955e644f0a24d310cc5a1cb2f20}