보안 (10)
2024-11-21 10:47:01

이번 글에서는 솔리디티 프로그래밍과 보안을 입문하기 위한 이더리움의 기본 개념과 특징을 서술한다.

1. 솔리디티란?

이더리움에서 제공하는 스마트 컨트랙트 개발 언어. 이더 [ETH]를 보내고 받는 데에 이용된다. 여기서 등장하는 용어는 낯설 수 있지만 추후에 다시 설명한다.

  • EVM [Ethereum Virtual Machine]을 타깃으로 디자인되었고
  • 튜링 완전성: 어떤 프로그래밍 언어 및 머신이 튜링 머신과 동일하 계산 능력으로 문제를 풀고 해결할 수 있다는 의미.
    • 솔리디티는 자체적인 튜링 완전 언어들을 지원하기 때문에 사실상 가능한 모든 형태의 거래를 코드로 작성할 수 있다.

2. 이더리움이란?

암호화폐라고 하면 떠오르는 것이 크게 두 개 있다.

  1. 비트코인
  2. 이더리움

거래를 해본 사람도 있을 것이고 들어만 본 사람도 있을 것이다. 적어도 프로그래밍 및 블록체인을 몰라도 이름은 알 것이라 생각한다. 사실 이더리움을 개념적으로 이해하는 것은 상당히 복잡하다(

적어도 나는 그렇다

)

우선 암호화폐를 정의할 필요가 있겠다.
암호화폐는 공개키 암호화를 통해 안전하게 전송하고 해시 함수를 이용해 쉽게 소유권을 증명해낼 수 있는 자산이다. 다르게 말하면 검산은 쉬운데 역산은 어렵다는 것이다.

이더리움도 비트코인과 같은 암호화폐이다.
그러나 비트코인은

    1. 튜링 불완전성: 스크립트로 작성된 비트코인은 비교적 단순한 편이라 화폐 이상의 기능을 수행하기 어렵다.
    1. 상태표현제한: 표현 가능한 상태가 사용완료 or 안함 둘뿐이라 잔액을 일부 남기고 계약이 불가하다.
    1. Blockchain-blindness: UTXO(비트코인 잔액)이 블록헤더 데이터들을 해독하지 못해서 화폐 기능 이외 다른 분야의 어플리케이션을 만들기 어렵다.

위처럼 비트코인은 단순하고 제한적이다.
이러한 배경에서 더 세련된 언어로 분산 어플리케이션 [Decentralized Application; dApp] 을 이용할 수 있는 플랫폼을 만들자! 는 시도에서 나온 게 블록체인이다.

이것들을 구현하기 위해 사용되는 기술들이 있다.
비트코인은 스크립트를 사용하여 튜링불완전한데,
이더리움은

  • Solidity
  • Serpent
    등을 이용하기 떄문에 튜링완전하고, 이게 곧 Smart Contract를 가능하게 한다.

3. 이더리움의 기술

GAS

이더리움 플랫폼에서는 이더(ether)라는 자체 화폐토큰이 있고 이더를 가지고 GAS라는 어플리케이션을 구입한다. 이것은 이더리움이 Smart Contract를 하는 데 연산력과 저장공간 제공을 위한 연료로 쓰인다.

Smart Contract

블록체인 기술을 활용해 제3의 인증기관 없이 개인 간 계약이 이루어질 수 있도록 하는 기술이다.

스마트 컨트랙트는 다음과 같은 성질을 가진다.

    1. 관측가능성(obsevability): 서로의 계약 이행 가능성을 관찰하거나 성과를 입증할 수 있어야 함
    1. 검증가능성(verfifiability): 계약을 이행하거나 위반할 경우 계약 당사자들이 이를 알 수 있어야함
    1. 프라이버시(privity): 계약 내용은 계약에 필요한 당사자만 알 수 있어야 함
    1. 강제 가능성(enforceability): 계약이 이루어질 수 있도록 구속력이 있어야 함

4. 이더리움의 블록

이더리움의 블록은 함수처럼 작동한다.

124번 블록이 상태 1에서 2로 바꿔주는 역할을 하는데,
여기서 상태 1은 블록1

123이 연결된 블록체인 상태를 의미하고, 상태 2는 1

124가 연결된 블록체인 상태이다.
이가 의미하는 건 블록이 추가될 때마다 상태가 계속해서 변한다는 것이다.

이러한 상태 변환 연산을 수만 개의 노드가 하나의 컴퓨터처럼 연산한다. 수만 개의 노드가 똑같은 연산을 중복하여 똑같은 데이터를 가지고 항상 동일한 상태의 합의에 도달한다.

이에 필요한 연산력을 아까 말했던 GAS의 구입을 통해 제공받는다.

5. 이더리움과 계좌

이더리움의 구성은
이더리움 = 이더 + 계좌 + GAS
로 표현될 수 있다.

아까도 언급했지만

  • 이더: 이더리움의 자체토큰
  • 계좌: 말그대로 이더를 가지고 있는 계좌
  • GAS: 연산력에 필요한 연료와 같은 것

이 중 계좌는 다음과 같이 다시 구분된다.

외부 소유계좌(EOA)

  • 개인키를 가지고 통제하는 계좌
  • 이더를 주고받을 수 있는 일반적인 은행 계좌와 비슷

계약계좌

  • 개인키가 아니라 계약 코드에 의해서 통제
  • 계약 코드계약 정보 저장 공간을 가지고 있고, 내부 저장공간의 데이터를 읽고 쓸 수 있다.

EOA는 일반적인 은행 계좌와 비슷한 개념이지만, 계약계좌는
GAS를 구입해 계약 코드를 걸어놓으면 코드의 조건이 만족될 때에만 계약계좌가 메시지를 받고 계약의 내용이 실행된다.

예를 들면 '과속을 했을 때' '1ETH를 부과하는 계약 코드'를 걸어놓으면 과속을 했을 때 메시지를 받고 1ETH가 계약계좌에서 빠져나간다.
이러한 계약 코드는 이더리움 플랫폼에서 정해놓은 헌법과도 같아서 누구도 통제가 불가능하다.

'보안 > web3' 카테고리의 다른 글

web3 시작하기  (0) 2024.11.18
2024-11-18 09:34:11
2024-11-12 20:05:18

https://dreamhack.io/wargame/challenges/1364

[datestring

Description Time is gold... but shell is diamond. Flag Format: DH{...}

dreamhack.io](https://dreamhack.io/wargame/challenges/1364)

파일을 다운받으면 따로 주어진 소스코드는 없다.

실행시

./datestring
Calendar v0.1
Year: 2024
Month: 1 
Day: 12
Hour: 19
Minute: 46
Second: 19
Formatted date: Fri Jan 12 19:46:19 2024

상단과 같이 나옴.

먼저 gdb에 넣어봤었는데 flag라는 함수가 있다는 걸 찾음.

flag호출 시 쉘을 취득가능한듯함.

flag호출 조건을 ida로 좀 더 편하게 볼 수 있는데 결론적으로

printf("Formatted date: %s", v16);
  if ( monthminusone == 11 && date_1 == 25 && !v15 && valuezero )
  {
    puts("A Present for Admin!");
    flag("A Present for Admin!");
  }

변수명을 조금 바꿔보면 month: 12, date: 25이고, 처리해야되는 부분은 !v15 이면서 valuezero를 0이 아닌 값으로 바꾸기.

 v15 = (year / -100 + year / 4 + 23 * month / 9 + date + 4 + year / 400) % 7;
  calendar(v16, v11);

v15는 이 부분을 처리해줘야함. 저게 0이 되면 된다.
해석하면 12/25가 일요일인 연도를 찾는 문제다

그런데 아무 연도나 입력하면 valuezero값이 안 덮어져서 플래그를 얻을 수 없다.
calendar를 이용해 버퍼오버플로우를 일으켜서 0이 아니도록 덮어버리면 될 일이다.
따라서 특정 연도라기보단 매우 크면서 12/25가 일요일인 연도 찾기를 하면됨
100000000 이상이면 무관하다.

가장 작은 친구로는

Year: 100000005
Month: 12
Day: 25
Hour: 2
Minute: 2
Second: 2
Formatted date: Sun Dec 25 02:02:02 100000005
A Present for Admin!

가 있겠다.
저 문구가 나오면 쉘을 취득했으니 플래그를 cat 하면 끝

2024-09-01 21:53:12

문제 파일을 다운받고, 실행하면 아무것도 없는 그냥 빈 화면이 나온다.
이렇게 패킹된 것은 ida로 열어서 정적분석해도 큰 의미는 없을 것 같다,
바로 x32dbg를 켜서 파일을 올리면

위와 같은 화면. 현재 ep는 0040A04B.

이 상황에서 아래로 쭉 내려서 확인을 해 보았다. OEP는 아마 현재 주소부터 어느 지점까지 실행되다가 특정 주소로 이동하는 지점이 있을 것이다. 실행의 마지막에 점프되는 주소가 oep이지 않을까 하는 생각임.

점프문이 있는지 살펴보면서 내려봤는데,

저 마지막 점프문이 의심스럽다.
브레이크포인트 걸고 실행해주자.
실행 결과 무사히 브레이크지점까지 실행되었고, 이제 한 줄 더 실행하면

00401150으로 점프되었다.
여기가 oep일 것으로 예상
일단 생각한 로직은 oep로 가려면 현재 주소에서 어느 부분까지가 반복문이든 뭐든 실행되다가, 결국은 점프문으로 실제 oep로 점프할 거라고 생각해서 점프되는 부분을 찾았고, 그 부분이
위에 브레이크포인트 걸어준 부분입니다.

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10
2024-08-18 20:47:15
  • (13.1~13.6)

13.1~13.2 PE File Format

PE 파일 소개

  • PE(portable executable): windows 운영체제에서 사용되는 실행 파일 형식
  • 32비트의 실행 파일을 의미한다.
  • 64비트의 경우 PE+, PE32+ 라고 부름

PE File Format

PE 파일의 종류

  • 실행 계열: EXE, SCR
  • 라이브러리 계열: DLL, OCX, CPL, DRV
  • 드라이버 계열: SYS, VXD
  • 오브젝트 파일 계열: OBJ(이걸 제외하고 위 모든 것들은 실행 가능함)

헤더

  • PE 헤더: 어떻게 메모리에 적재되고, 어디서부터 실행되어야 하는지, 실행에 필요한 DLL들은 어떤 것이 있는지 등등 exe파일이 실행되기 위해 필요한 모든 정보가 적혀있다고 할 수 있다.

PE파일의 기본 구조

실습 내용에서 확인한 바와 같이 notepad.exe는 일반적인 PE파일의 구조를 따르는데
DOS header ~Section header: PE헤더
그 밑의 sections들은 합쳐 PE바디 라고 한다.

  • 파일과 메모리: 파일에서는 offset, 메모리에서는 VA(Virtual Address, 절대주소)로 표현되기 때문에 파일이 메모리에 로딩되면 모양이 달라진다.

주소(VA&RVA)

- VA(Virtual Address)

프로세스 가상 메모리의 절대 주소

-RVA(Relative Virtual Address)

어느 기준 위치(Image Base)에서부터 상대주소
따라서 관계식을 아래와 같이 쓸 수 있다.

  • RVA + Image Base = VA

PE헤더 내의 정보는 RVA로 된 것이 많다.
PE파일이 프로세스 가상 메모리의 특정 위치에 로딩되는 순간 그 위치에 이미 다른 PE파일이 로딩되어 있을 수 있다. 이런 경우에 재배치(relocation)를 통해 비어 있는 다른 위치로 파일을 로딩시켜야하는데, VA( 절대 주소) 로 헤더 정보들이 표현되어있으면 정상적인 액세스를 진행할 수 없다.
RVA를 쓰면 relocation을 해도 ‘기준위치에 대한 상대주소’는 변하지 않기 때문에 정상적으로 작동된다.


13.3 PE 헤더

PE헤더는 많은 구조체로 이루어져있다. 여기서 다룰 것은

  • DOS Header
  • DOS Stub
  • NT Header
    • File Header
    • Optional Header

1. DOS Header

  • 마이크로소프트가 PE file format을 만들 때 당시 널리 사용되던 DOS하위 호환성 고려해서 만듦-> PE헤더의 맨 앞에 IMAGE_DOS_HEADER구조체가 있다.(DOS EXE HEADER의 확장)
  • IMAGE_DOS_HEADER의 구조체 크기는 고정되어있고 40이다.
  • 중요 멤버로는 e_magic, e_1fanew가 있는데
    • e_magic: 모든 PE파일은 시작 부분인 e_magic에 DOS signature “MZ”(마크 주비코브스키의 이니셜이다)(4D5A)가 있음
    • e_1fanew: 끝부분인 e_1fanew가 가리키는 위치에 NT Header(IMAGE_NT_HEADERS)구조체가 존재해야한다.

2. DOS Stub

  • DOS Header밑에 위치한다.
  • 없어도 실행에는 문제가 없기 때문에 존재 여부는 옵션이고, 크기도 일정하지 않다.
    • 존재 여부가 옵션이라 개발 도구에서 지원해줘야 한다.(VB, VC++등은 기본 지원한다고 한다.)
  • 코드와 데이터의 혼합으로 이루어져 있다.

3. NT Header

  • DOS Header를 설명하면서 간략히 언급하였는데, e_1fanew가 가리키는 위치에 이게 있다.
  • IMAGE_NT_HEADERS는 3개의 멤버로 되어있는데
      1. Signature: 50450000h(“PE”00)값을 가진다.
      1. File Header
      1. Optional Header

3-1. NT Header -File Header

  • 파일의 개략적인 속성을 나타낸다.
  • 아래 멤버가 정확히 세팅되어있지 않으면 파일은 정상적으로 실행되지 않는데
    • Machine, NumberOfSections, SizeOfOptionalHeader, Characteristics

- Machine

CPU별로 고유한 값을 가진다. 인텔 x86은 14C의 값을 가진다.

- NumberOfSections

PE파일은 코드, 데이터 리소스 등이 각각의 섹션에 나뉘어서 저장되는데, 이떄 그 섹션의 수를 의미한다. 이 값은 0보다 커야하고, 정의된 섹션과 실제 섹션이 다르면 실행 에러가 발생한다.

- SizeOfOptionalHeader

IMAGE_NT_HEADER32의 크기를 나타내는 멤버인데, 이건 C언어의 구조체이기 때문에 이미 크기가 결정되어있다. 그럼에도 이게 따로 필요한 이유는 Windows의 PE로더가 SizeOfOptionalHeader값을 보고 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 인식하기 때문이다.
64비트에서(PE32+)는 IMAGE_OPTIONAL_HEADER64구조체를 사용하는데, IMAGE_OPTIONAL_HEADER32와 크기가 다르기 때문에 SizeOfOptionalHeader멤버에 구조체 크기를 명시한다.

-Characteristics

파일의 속성을 나타내는 값이다.
실행 가능 여부(executable or not), DLL or not등의 정보들이 bit OR형식으로 조합된다.


3-2. NT Header -Optional Header

  • PE 헤더 구조체 중에 가장 크기가 큰 IMAGE_OPTIONAL_HEADER32
  • 아래 멤버들이 잘못 세팅되면 파일이 정상 실행되지 않는다
  • Magic, AddressOfEntryPoint, ImageBase, SectionAlignment, FileAlignment, SizeOfImage, SizeOfHeader, Subysystem, NumberOfRvaAndSizes, DataDirectory

1. Magic

IMAGE_OPTIONAL_HEADER32구조체의 경우 10B, 64의 경우 20B값을 가진다.

2. AddressOfEntryPoint

EP(Entry Point)의 RVA값을 가지고 있다. 이 값은 프로그램에서 최초로 실행되는 코드의 시작 주소이므로 매우 중요하다.

3. ImageBase

프로세스의 가상 메모리는 0~FFFFFFFF범위라서 매우 광활한데, ImageBase는 이 범위에서 PE파일이 로딩되는 시작 주소를 나타낸다.

  • EXE, DLL -> 0~7FFFFFFF(user memory)
  • SYS -> 80000000~FFFFFFFF(kernel memory)
    일반적으로 개발 도구가 만들어내는 EXE파일의 ImageBase는 00400000, DLL 파일의 ImageBase 값은 10000000이라고 한다. PE로더는 PE파일을 실행시키기 위해 프로세스를 생성하고 파일을 메모리에 로딩 후 EIP 레지스터 값을 ImageBase + AddressOfEntryPoint 값으로 세팅한다.

4. SectionAlignment, FileAlignment

PE파일의 Body는 섹션으로 나눠지는데

  • 파일에서 섹션의 최소단위 -> FileAlignment
  • 메모리에서 섹션의 최소단위 -> SectionAlignment
    한 파일에서 위 두 값은 같을 수도, 다를 수도 있다.
    그러나 파일과 메모리의 섹션 크기는 반드시 각각 FileAlignment, SectionAlignment의 배수가 되어야 한다.(최소 단위의 기준이므로)

5. SizeOfImage

PE파일이 메모리에 로딩되었을 때 가상메모리에서 PE Image가 차지하는 크기를 나타낸다. 일반적으로 파일의 크기와 메모리에 로딩된 크기는 같지 않다고 한다.

6. SizeOfHeader

PE 헤더의 전체 크기를 나타낸다. 섹션 크기에서와 마찬가지로 FileAlignment의 배수 크기여야 한다. 파일 시작에서 SizeofHeader 옵셋만큼 떨어진 위치에 첫 번째 섹션이 위치한다.

7. Subysystem

Subysystem 값을 보고 시스템 드라이버인지, 일반 실행 파일인지 구분 가능하다.

8. NumberOfRvaAndSizes

IMAGE_OPTIONAL_HEADER32 구조체의 마지막 멤버 DataDirectory 배열의 개수를 나타낸다.
구조체 정의에 배열 개수가 IMAGE_NUMBEROF_DIRECTORY_ENTRIES(16)라고 명시되어 있으나 PE 로더는 이 값을 보고 배열의 크기를 인식한다.

9. DataDirectory

위에서 잠깐 언급했지만 IMAGE_DATA_DIRECTORY구조체의 배열이다. 배열의 각 항목마다 정의된 값을 가진다.
Directory란 어떤 구조체의 배열로 보면 되는데, 이 중 IMPORT, EXPORT Direcotry구조는 PE헤더에서 매우 중요하다.

DataDirectory[0] = EXPORT Directory
DataDirectory[1] = IMPORT Directory
DataDirectory[2] = RESOURCE Direcotry
DataDirectory[3] = EXCEPTION Directory
...
DataDirectory[9] = TLS Directory
...
DataDirectory[F] = Reserved Directory

섹션 헤더

각 섹션의 속성을 정의한 것이 섹션 헤더이다.
PE파일은 코드, 데이터, 리소스 등을 각각 섹션으로 나눠서 저장한다고 했는데, 책에서 설명하기로 이렇게 PE파일을 여러 개의 섹션 구조로 만드는 이유는 프로그램의 안정성에 있다고 한다.
코드와 데이터가 하나의 섹션에 있다면 복잡하기도 하지만 안정성에 문제가 생길 수 있다. 데이터에 오버플로우와 같은 문제가 생겼을 때 코드와 리소스도 하나의 섹션에 모두 모여있다면 오버플로우가 코드를 그대로 덮어써버리게 될 것이다.
이러한 이유로 PE File Format 설계자들은 비슷한 성격의 자료를 섹션으로 나눠두었다. 따라서 섹션의 속성을 나타내기 위해 각각의 섹션에 섹션 헤더가 필요한 것이다.
코드, 데이터, 리소스 각각의 섹션마다 특성이나 접근 권한을 다르게 설정할 필요가 있다.

섹션 헤더는 각 섹션별 IMAGE_SECTION_HEADER 구조체의 배열로 되어있는데,

이 중 알아야 할 중요 멤버는

  • VirtualAddress, PointerToRawData는 각각 SectionAlignment와 FileAlignment에 맞게 결정된다.(아무 값이나 들어가선 안된다.)
  • VirtualSize와 SizeOfRawData는 일반적으로 다른 값을 가지는데, 위에서 언급했듯 파일에서의 섹션 크기와 메모리에 로딩된 섹션의 크기가 다르다는 말이다.
  • Characteristics는 아래 표시된 값들의 조합(bit OR)로 이루어진다.

Name멤버는 NULL로 끝나야 한다거나 ASCII값만 와야한다는 제한도 없는데, PE 스펙에는 섹션 네임에 대해 명시적인 규칙이 없어서 어떠한 값을 넣어도 상관없다. 심지어 NULL값도 가능하다!

13.4 RVA2RAW

PE파일이 메모리에 로딩되었을 때 각 섹션에서 메모리의 주소(RVA)와 파일 옵셋을 매핑하는 것을 RVA to RAW라고 부른다.방법은

  1. RVA가 속해 있는 섹션을 찾고
  2. 비례식을 통해 파일 옵셋(RAW)을 계산

IMAGE_SECTION_HEADER구조체에 의하면 비례식은

RAW - PointerToRawData = RVA - VirtualAddress
RAW = RVA - VirtualAddress + PointerToRawData

13.5 IAT

IAT란 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블이다.

DLL

IAT를 설명하기 위해 DLL의 개념을 먼저 짚고 넘어가자.
DLL(Dynamic Linked Library), 동적 연결 라이브러리는 메모리 절약을 위해 고안되었다고 볼 수 있는데, 이게 있기 전까지는 C라이브러리의 함수의 바이너리 코드를 그대로 가지고 와서 썼다. 그런데 멀티 태스킹 시 여러 프로그램이 동시에 실행되는데 도일한 라이브러리가 매번 포함되는 경우 심각한 메모리 낭비가 발생한다.
그래서 프로그램에 라이브러리를 포함시키지 말고 별도의 파일로 구성하여 필요할 떄마다 불러오자는 전략을 취했는데, 이 파일이 DLL이다.

DLL 로딩 방식

  • Explicit Linking: 프로그램에서 사용되는 순간에 로딩하고 사용이 끝나면 메모리에서 해제되는 방법
  • Implicit Linking: 프로그램 시작 시 로딩되어 프로그램 종료 시 해제됨
    DLL은 위 두 로딩 방식이 있는데 IAT는 Implicit Linking에 대한 매커니즘을 제공한다.

notepad에서 살펴보면 01001104주소에 있는 값을 가져와서 호출하게 된다. 위 주소의 값은 7C8107F0인데 이걸 바로 CALL 7C8107F0으로 호출하지 않는 이유는

  • notepad.exe 프로그램을 컴파일하는 순간에 어떤 윈도우즈 버전, 어떤 언어, 어떤 서비스팩 등 어떤 환경에서 실행될지 알 수 없는데, 위와 같은 환경의 변화에 따라 kernel32.dll버전과 CreateFileW함수의 주소가 달라지기 때문이다. 따라서 주소가 저장될 위치만 준비해야하고, 실제 값은 하드코딩할 수 없다.
  • DLL Relocation문제가 있다. 로딩하려는 위치가 이미 사용중인 메모리인 경우 비어있는 메모리 공간에 로딩시켜주게 되므로(relocation) 하드코딩할 수 없다.

IMAGE_IMPORT_DESCRIPTER

PE파일은 자신이 어떤 라이브러리를 임포트하고 있는지 IMAGE_IMPORT_DESCRIPTER에 명시하고 있다.

'보안 > 리버싱' 카테고리의 다른 글

reversing.kr - easy unpack  (0) 2024.09.01
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10
2024-08-18 20:25:34
  • exe파일이므로 마찬가지로 윈도우에서 실행하자.
  • 사용 도구는 x32dbg
  • 실행하면

와 같이 이름을 먼저 묻고 시리얼을 묻는다.
조건을 만족하지 않는 값을 넣으면 Wrong이 출력된다.

같이 첨부된 리드미 파일을 열어보면

Find the Name when the Serial is 5B134977135E7D13
라고 한다. 시리얼이 주어져있고, 이름을 찾아내면 되는듯하다

ida를 열어, 메인함수를 디컴파일하면

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // ebp
  int i; // esi
  int v6; // [esp+0h] [ebp-13Ch]
  int v7; // [esp+0h] [ebp-13Ch]
  int v8; // [esp+Ch] [ebp-130h]
  char v9[100]; // [esp+10h] [ebp-12Ch] BYREF
  char Buffer[197]; // [esp+74h] [ebp-C8h] BYREF
  __int16 v11; // [esp+139h] [ebp-3h]
  char v12; // [esp+13Bh] [ebp-1h]

  memset(v9, 0, sizeof(v9));
  memset(Buffer, 0, sizeof(Buffer));
  v11 = 0;
  v12 = 0;
  LOWORD(v8) = 8208;
  BYTE2(v8) = 48;
  sub_4011B9(aInputName, v6);
  scanf("%s", v9);
  v3 = 0;
  for ( i = 0; v3 < (int)strlen(v9); ++i )
  {
    if ( i >= 3 )
      i = 0;
    sprintf(Buffer, "%s%02X", Buffer, v9[v3++] ^ v9[i - 4]);
  }
  memset(v9, 0, sizeof(v9));
  sub_4011B9(aInputSerial, v7);
  scanf("%s", v9);
  if ( !strcmp(v9, Buffer) )
    sub_4011B9(aCorrect, v8);
  else
    sub_4011B9(aWrong, v8);
  return 0;
}

위와 같다.
사용자가 입력한 name을 받아 v9에 저장하고, 반복문 내 연산을 수행한 후 Buffer에 저장하는 것으로 보인다. 그 후 v9와 Buffer 를 비교하여 일치 여부에 따라 Correct, Wrong이 나타나는 듯하다.
리드미에 있는 시리얼 값을 이용하여 name을 역연산하면 해결할 수 있을 것이다.

x32dbg를 열어서 확인해보면

브레이크 포인트가 걸린 곳은 scanf로 name을 받는 곳이고, 계속 한줄씩 실행하다보면 해당 지점으로 계속 돌아온다. 이 부분이 위의 반복문임을 확인할 수 있다.


전체 뷰를 확인하면 위와 같다.

# 키값으로 이름 찾는 알고리즘 
# find name algorithm using key value
# 5B134977135E7D13
hexx = ['5b','13','49','77','13','5e','7d','13']
count = 1
full = ''

for x in hexx:
    for i in range(0,200):
        a = (16 * count) ^ i
        a = hex(a)
        a = a[2:]

        if a == x:
            print('원래값:',i,"hexx값:",x,'count값체크:',count)
            full = full + chr(i)

            if count == 3:
                count = 1                
                break

            else:
                count = count + 1                
                break
print(full)
원래값: 75 hexx값: 5b count값체크: 1
원래값: 51 hexx값: 13 count값체크: 2
원래값: 121 hexx값: 49 count값체크: 3
원래값: 103 hexx값: 77 count값체크: 1
원래값: 51 hexx값: 13 count값체크: 2
원래값: 110 hexx값: 5e count값체크: 3
원래값: 109 hexx값: 7d count값체크: 1
원래값: 51 hexx값: 13 count값체크: 2
K3yg3nm3

'보안 > 리버싱' 카테고리의 다른 글

reversing.kr - easy unpack  (0) 2024.09.01
1주차  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10
2024-08-18 18:03:45

exe파일이므로 윈도우에서 실행하면
input를 묻고

IDA를 키면

input을 묻는 부분을 바로 찾을 수 있다.


메인함수를 디컴파일해보니 위와 같다.
추정컨대 sub_140011B0으로 인풋 메시지를 출력하고
sub_14001210으로 값을 받아서 저장하는 것으로 보인다.

우리가 알아야 하는것은 Correct가 뜨는 조건인데 그 조건과 관련된 함수는
sub_14001000으로 보인다.

그 함수도 디컴파일했는데 결과가 위와 같다. 인풋값을 ac배열과 비교하는 로직이다.
따라서 ac배열을 조사해볼 필요가 있다.

ac배열을 클릭해서 들어가보면
Comp4re_the_arr4y를 확인할 수 있다.

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10
picoCTF ARMssembly0  (0) 2024.08.08
2024-08-11 20:31:58
  • exe 파일이므로 윈도우에서 실행해보자.
  • 실행하면 패스워드를 받는 구조인데,

비밀번호를 현재는 모르니 아무거나 입력하면 아래와 같은 메시지박스를 출력한다.

 

 

 

 

따라서 Incorrect Password를 검색하여 그 전후로 비밀번호를 검증하는 로직을 찾을 수 있을 것이다.
틀린 경우에 위와 같은 메시지박스를 출력할 것이고, 맞을 때는 현재는 알 수 없다

.

x32dbg에서 Incorrect Password를 찾으면 아래와 같은 위치에 있다.

 

 

 

 

조금 올려보면 Congratulation이 있다. Congratulation이 되는 조건을 찾으면 될 것으로 보인다. 입력 방식을 보아 Congratulations 위로 문자열을 비교하는 로직들을 확인하면 될 것 같다.
그 위로 esp+4와 E(코멘트로 45:E)를 비교하는 로직이 있다.

 

 

더 더 위로 올려보면

 

 

위와 같이 어떤 문자열과 비교하는 과정들이 있다.
주석처리를 통해 저것들이 무엇인지 알 수 있으며, 플래그가 저기 적힌 a, 5y, R3versing과 관련이 있을 것으로 보인다.

Ea5yR3versing을 입력하게 되면

 

 

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
ELF x86 - Basic  (0) 2024.08.10
picoCTF ARMssembly0  (0) 2024.08.08
2024-08-10 00:53:28

파일을 다운받고, 압축을 해제하면 ch2.bin이 나온다.
제목에서도 예상했지만 32비트 ELF파일이다.

file ch2.bin
ch2.bin: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.8, with debug_info, not stripped

프로그램을 미리 실행해보면, username을 먼저 받고 username이 틀리면 그 이후로 넘어가지 않는다.
여기까지만 보고 예상해 보건대 username과 password를 모두 맞춰야 통과되는 것 같다.

그런데,
이걸 IDA로 열면 사실 굉장히 직관적으로 답이 보인다...
아이다를 열면 성공적으로 메인함수로 바로 보여준다.

열자마자 john, the ripper라는 문자열이 보인다.
그리고 아래에 아까 봤던 익숙한 시작 화면 문자열이 보인다. username을 받고 분기점을 보니 username이 맞을 때 password를 입력할 수 있도록 넘어가는 것이다.

usename에 john을, password에 the ripper를 넣으면

./ch2.bin
############################################################
##        Bienvennue dans ce challenge de cracking        ##
############################################################

username: john
password: the ripper
Bien joue, vous pouvez valider l'epreuve avec le mot de passe : 987654321 !

플래그가 987654321이라고 한다. 대충지었군..

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
picoCTF ARMssembly0  (0) 2024.08.08
2024-08-08 10:16:41

What integer does this program print with arguments 266134863 and 1592237099? File: chall.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb})


[WRITEUP]

파일을 다운받고

file chall.S
chall.S: assembler source, ASCII text

확인해보면 어떤 어셈블리 소스코드임을 확인할 수 잇다.

cat chall.S

로 내용을 출력해보면

cat chall.S          
    .arch armv8-a
    .file    "chall.c"
    .text
    .align    2
    .global    func1
    .type    func1, %function
func1:
    sub    sp, sp, #16
    str    w0, [sp, 12]
    str    w1, [sp, 8]
    ldr    w1, [sp, 12]
    ldr    w0, [sp, 8]
    cmp    w1, w0
    bls    .L2
    ldr    w0, [sp, 12]
    b    .L3
.L2:
    ldr    w0, [sp, 8]
.L3:
    add    sp, sp, 16
    ret
    .size    func1, .-func1
    .section    .rodata
    .align    3
.LC0:
    .string    "Result: %ld\n"
    .text
    .align    2
    .global    main
    .type    main, %function
main:
    stp    x29, x30, [sp, -48]!
    add    x29, sp, 0
    str    x19, [sp, 16]
    str    w0, [x29, 44]
    str    x1, [x29, 32]
    ldr    x0, [x29, 32]
    add    x0, x0, 8
    ldr    x0, [x0]
    bl    atoi
    mov    w19, w0
    ldr    x0, [x29, 32]
    add    x0, x0, 16
    ldr    x0, [x0]
    bl    atoi
    mov    w1, w0
    mov    w0, w19
    bl    func1
    mov    w1, w0
    adrp    x0, .LC0
    add    x0, x0, :lo12:.LC0
    bl    printf
    mov    w0, 0
    ldr    x19, [sp, 16]
    ldp    x29, x30, [sp], 48
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits

와 같이 나오는 것을 볼 수 있다. (코드가 짧아서 전체를 집어넣었다.)

func1 함수

func1:
    sub    sp, sp, #16
    str    w0, [sp, 12]
    str    w1, [sp, 8]
    ldr    w1, [sp, 12]
    ldr    w0, [sp, 8]
    cmp    w1, w0
    bls    .L2
    ldr    w0, [sp, 12]
    b    .L3

위에서 핵심은 cmp w1, w0이다.
w0이 w1보다 작으면 L2로 분기한다고 한다.

L2, L3

.L2:
    ldr    w0, [sp, 8]
.L3:
    add    sp, sp, 16
    ret
    .size    func1, .-func1
    .section    .rodata
    .align    3
.LC0:
    .string    "Result: %ld\n"
    .text
    .align    2
    .global    main
    .type    main, %function

main

main:
    stp    x29, x30, [sp, -48]!
    add    x29, sp, 0
    str    x19, [sp, 16]
    str    w0, [x29, 44]
    str    x1, [x29, 32]
    ldr    x0, [x29, 32]
    add    x0, x0, 8
    ldr    x0, [x0]
    bl    atoi
    mov    w19, w0 
    ldr    x0, [x29, 32]
    add    x0, x0, 16
    ldr    x0, [x0]
    bl    atoi 
    mov    w1, w0
    mov    w0, w19
    bl    func1 ; func1호출
    mov    w1, w0 
    adrp    x0, .LC0
    add    x0, x0, :lo12:.LC0
    bl    printf
    mov    w0, 0
    ldr    x19, [sp, 16]
    ldp    x29, x30, [sp], 48
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits

결과적으로 들어오는 인자 중 더 큰 값이 반환된다.
따라서 위 인풋에서 들어오는 값 중 더 큰 것은 1592237099 이므로,
플래그 형식에 맞게 32비트 16진수로 바꾼 플래그는
picoCTF{5ee79c2b}

'보안 > 리버싱' 카테고리의 다른 글

1주차  (0) 2024.08.18
easy keygen  (0) 2024.08.18
rev-basic2  (0) 2024.08.18
Easy Crack Me  (0) 2024.08.11
ELF x86 - Basic  (0) 2024.08.10