CTF

Google Capture The Flag 2020] Basics (Verilog)

wsoh9812 2021. 1. 18. 19:33

이 문제를 택한 이유는 최근 CTF에서 Verilog언어를 사용한 문제가 종종 출제되고 있어서 문법도 공부해볼겸 적어봅니다.

( 참고로 제기준에서 이해하기 쉽게 적은 것이라 틀린부분이 있을 수 도있습니다.)

 

파일은 Verilog언어로 하드웨어 동작?을 구현한 문제입니다.

이를 분석해서 input을 넣으면 flag가 나옵니다.

 

파일은 main.pp와 check.sv가 주어집니다.

 

#include "obj_dir/Vcheck.h"

#include <iostream>
#include <memory>

int main(int argc, char *argv[]) {
    Verilated::commandArgs(argc, argv);
    std::cout << "Enter password:" << std::endl;
    auto check = std::make_unique<Vcheck>();

    for (int i = 0; i < 100 && !check->open_safe; i++) {
        int c = fgetc(stdin);
        if (c == '\n' || c < 0) break;
        check->data = c & 0x7f;
        check->clk = false;
        check->eval();
        check->clk = true;
        check->eval();
    }
    if (check->open_safe) {
        std::cout << "CTF{real flag would be here}" << std::endl;
    } else {
        std::cout << "=(" << std::endl;
    }
    return 0;
}

main.cpp

 

main에서 보아야할 것은 문자를 하나씩 입력받는 다는 것입니다.

[*] c&7f이 중요한 부분입니다. input값이 1바이트 즉 8비트인데 거기서 7비트만 data에 넣겠다는 것입니다.

 

module check(
    input clk,

    input [6:0] data,
    output wire open_safe
);

reg [6:0] memory [7:0];
reg [2:0] idx = 0;

wire [55:0] magic = {
    {memory[0], memory[5]},
    {memory[6], memory[2]},
    {memory[4], memory[3]},
    {memory[7], memory[1]}
};

wire [55:0] kittens = { magic[9:0],  magic[41:22], magic[21:10], magic[55:42] };
assign open_safe = kittens == 56'd3008192072309708;

always_ff @(posedge clk) begin
    memory[idx] <= data;
    idx <= idx + 5;
end

endmodule

check.sv

 

check.sv가 Verilog언어로 구현된 코드입니다.

코드를 끊어서 전체적인 문법을 훑어본다음 흐름을 정리해보겠습니다.

 


코 드 분 석


 

그림.1

[6:0]표기7비트를 표시합니다.  -> 7비트의 크기를 data에 저장한다는 것이겟죠.

 

wire은 reg와 가장 많이쓰이는 자료형?

reg는 c언어로 그냥 int,char로 생각하면 되고 

wire은 'element들을 연결해준다.'인데 연속적인 할당이라고 생각하면 될것같습니다.

 

예를들어 

wire a;

wire b; 

이렇게 선언하게되면 

메모리상 a b가 서로 연속으로 이어진다 라고 생각하면 됩니다.

a--------b 이런식으로 말이죠. 

 


 

그림.2

reg [6:0] memory [7:0]는 7비트의 크기를 가진 배열을 8개 생성해준다.

-> memory[8] 인데 크기는 7비트

 

reg [2:0] idx = 0은 Verilog의 비트 개념을 확실하게 알려준 녀석입니다.

-> 3비트의 크기를 가진 idx를 만들어주고 0을 넣겠다는 말이죠?

여기서 3비트로 만들 수 있는 큰 수는 7입니다.

idx= 10이 들어가면 터져버려서 idx는 2가 됩니다.

idx 3비트는 c로 구현하면 idx%8정도 되겠죠?

 


 

그림.3

memory는 7비트의 크기를 가진다고 했습니다. 

그것이 8개 있으니깐 56비트겠죠? 그래서 magic의 크기가 56비트인 것이고요.

wire은 위에서 봤듯이 연속적인 할당입니다. 

즉, 메모리에 'memory[0].memory[5].memory[6].memory[2].........'와 같은 형태로 연속적으로 할당이 됩니다.

 

 

이것 역시 위와 마찬가지입니다.

56비트의 크기를 가진 kittens에 magic[9:0](10비트) , magic[41:22](20비트) 이렇게 순차적으로 할당해줍니다.

 


 

그림.4

kittens랑 비교를 하는데 '56`d...'은 56비트의 크기를 나타낸 10진수입니다.

즉,  d 뒤로 3008192072309708이 10진수이고, 이것을 56비트로 표현한 것이랑 비교한다는 뜻입니다.

 

 

'00001010101011111110111101001011111000101101101111001100'이 위의 10진수를 56비트로 표현한 것입니다.

'이 2진수가 kittens랑 같아야 한다'라는 것이겟죠?

 

kittens는 어떻게 만들어지냐?

그림.3을 보시면 magic의 비트로 만들어집니다.

 


 

왼쪽부터 10비트가 magic[9:0]에 해당하고, 이후 20비트는 magic[41:22]에 해당하고.............

 

 

 

그림.5

그림.5와 같이말이죠!!!

 

근데 또 magic은 어디서 구해지냐..

그림.3을 memory에 의해 magic이 만들어지죠?

memory는 input값들 입니다.

 


 

 

그림.6

아직 설명드리지 않은 코드에서 input값이라는 것을 알 수 있습니다.

 

idx는 0 5 10 15 20.. 이렇게 증가합니다.!!!! 하지만, idx는 3비트라고 했죠? 10부터 터져버려 2가 되고, 15는 7이되고....

 

일단 input의 길이는 8입니다. 왜 그런지는 생략하겠습니다.(코드분석해보시면 알 수 있습니다.)

 

8개가 순차적으로 memory에 넣는다고 하면 index는 

'0 5 2 7 4 1 6 3' 이렇게 input값이 들어갑니다. 

( 인덱스 0에 첫번째 input, 5에 두번째 input, 2에 세번째 input )

 


 

자!!!!! 그림.5로 돌아가봅시다.

 

그림.7

정렬 전후를 살펴보십시오. 

 

결국 저희가 구해야하는 것은 그림.3입니다. 정렬된 magic을 구해서 다시 memory를 구해야합니다.

 

정렬된 magic은

 

'0110111 1001100 1011111 1101111 0100101 1111000 1011000 0101010'입니다. 

 

 

이제 정렬된 magic을 memory는 7비트이니 7비트씩 끊어서 문자로 확인해보면 

 

memory[0] = 7
memory[1] = *
memory[2] = o
memory[3] = x
memory[4] = %
memory[5] = L
memory[6] = _
memory[7] = X

 

이고 아까 위에서 그림.6에서 말한 인덱스로 맞추면 끝입니다.!

 

7LoX%*_x을 input으로 넣으면 플래그가 나옵니다.

FLAG : CTF{W4sTh4tASan1tyCh3ck?}

 

'CTF' 카테고리의 다른 글

CCE 2021 대회 후기  (0) 2021.09.26
FoobarCTF 2021 reversing Write-up (Z3)  (0) 2021.04.05
justCTF2020] debug_me_if_you_can ( LD_PRELOAD 후킹, ptrace )  (0) 2021.02.01
BambooFox CTF  (0) 2021.01.18