GrownUp 문제입니다.
IDA로 먼저 보면
먼저 18세 이상인지 물어보고, 이상이라면 Name을 물어보네요.
setup 함수도 있으니 확인해봅시다.
대충 뭔갈 많이 지정해주네요.
여기서 우리가 문제를 풀기 이전에 알아야 할 점은
read 함수는 문자열의 마지막에 null을 붙이지 않고 끝낸다.
strcpy 함수는 복사를 마친 후 마지막에 null을 붙인다.
라는 점입니다.
따라서 read함수에 꽉 채운 뒤 strcpy에 보내면, 한 바이트가 더 들어가서 저장된다는 것이죠.
https://sunrinjuntae.tistory.com/50
이분의 블로그를 통해서 이 점을 알게 되었습니다.
IDA를 통해 더 살펴봅시다.
이 부분에 flag가 숨겨져있습니다.
저기 FLAG{} 이런식으로 적혀있는데... 틀린겁니다 ㅠㅜ
여기 usr 변수와 qword_601160를 잘 보셔야합니다.
이런 코드로 사용되고 있는데, qword_601160은 나중에 gdb로 알아보겠지만 %s를 나타내기 때문에 사실상
printf("%s", usr);
이렇게 사용되고 있는것과 마찬가지입니다.
하지만 usr에서 qword_601160까지 데이터를 넘치게 할 수 있기 때문에 이를 이용하면 %s를 덮어씌우고 FSB를 일으킬 수 있습니다.
GDB로 자세히 살펴봅시다.
위에 조금 잘리긴 했는데 저기부터 보면 됩니다.
뭔지는 대충 알겠죠?
그리고 setup을 통해서 뭔가가 많이 진행되므로 저 부분에 브레이크를 걸어주고 실행해봅시다.
저기에 걸어주고 ni를 통해 함수를 실행해줍니다.
이후 usr이라고 적혀있는 0x6010e0 부분의 데이터를 여러개 찾아서 보게 되면,
이렇게 찾아볼 수 있게 됩니다.
저 위에 %s라고 했던 부분의 말이 맞았던 것이죠.
그냥 뭔갈 진행하려고 실행해도, FSB는 안됩니다.
저 %s 때문에요...
이제 확인할 것을 다 확인했고 알 것도 다 알았으니... 코드를 다시 한번 봅시다.
src 변수를 선언해줬습니다.
buf와 v6 v7 변수를 선언해주고 0으로 초기화까지 해줬습니다.
18살 이상인지 물어보고?
그에 대한 답변을 0x10만큼 read 함수로 받아온 뒤 buf에 저장합니다.
만약 buf 값이 Y나 y가 아니라면 return 0으로 프로그램을 끝내고,
그렇지 않다면 src 변수를 동적할당으로 0x84만큼 할당해줍니다.
그리고 name을 물어본 뒤에 src에 0x80만큼 read 함수로 받아오고
strcpy 함수로 src에 있는 값을 usr로 옮깁니다.
그리고 printf 함수로 Welcome을 출력하고, usr 변수를 또 printf 합니다.
마지막 printf에서 qword_601160, usr이라고 되어있는데,
이는 위에서 printf("%s", usr)인 것을 알 수 있었습니다.
이렇게 읽을 수 있겠네요...
그리고
이걸 또 잘 봐달라고 했었습니다.
우리는 저기 601160 부분을 덮어써서 null을 넣어주고, printf를 실행할 겁니다.
저 두 변수 사이의 간격은 0x80이고, src 변수에 값을 집어넣는 read함수의 크기도 0x80이라, 일반적으로 생각했을 때에는 방법이 없는 것 같아 보입니다.
read 함수는 문자열의 마지막에 null을 붙이지 않고 끝낸다.
strcpy 함수는 복사를 마친 후 마지막에 null을 붙인다.
이 부분을 이용하면 방법이 생기게 됩니다.
우선 순서가 src 받아오기, usr에 복사하기 순서로 read, strcpy가 각각 사용됩니다.
이걸 순서대로 잘 생각하게 되면,
read 함수에서 0x80만큼 꽉 채워서 데이터를 입력하게 된다면... strcpy를 거치면서 null이 추가되어 총 0x81이 usr에 입력될 수 있다는 것을 알 수 있습니다.
그리고 flag의 주소도 알기 때문에, FSB 방식으로 출력을 해줄 수 있습니다.
FSB는 %s, %x, %p와 같은 format string을 사용할 때마다 스택에 저장된 주소값, 데이터를 출력할 수 있다는 점을 이용하는 방식입니다. usr에 값을 넘치게 해서 %s 부분을 null로 바꿔버리게 되면, 이후 usr을 출력할 때 확장자 없이 그대로 printf(usr)로 만들어지게 됩니다.
이를 이용해서 usr에 %x와 같은 format을 미리 넣어두게 되면 저 %x한 값이 출력되게 됩니다.
이런 값이
이런식으로 출력될 수 있게 됩니다.
payload는 다음과 같습니다.
설명을 해보자면,
우선 처음 18세 이상인지를 물어볼 때 넘어가기 위해서 Y를 한 번 적어줍니다.
그리고 AAAAAAA, A를 총 7번 입력해서 64비트에서 하나의 주소가 가지고 있는 8바이트를 채워줘야합니다.
그 뒤에 0x601080을 리틀 엔디안 방식으로 변환하여 입력해줍니다.
이 부분이 스택에 저장되어 나중에 포맷 스트링 버그를 일으킬 때 받아올 수 있는 주소값들입니다.
이 다음, name을 물어봤을 때 dummy를 앞에 충분히 넣어주고, %p를 여러번 반복해서 스택 내에서 우리가 입력한 flag의 주소가 담긴 부분이 몇번째 포맷인지 찾아줍니다.
해당 부분에서는 9번째 주소값이 찾고자 하던 부분이라는 것을 알 수 있습니다.
그리고 0x80을 꽉 채워야지만 이후에 null값을 넘길 수 있기 때문에 문자열에 남은 길이를 dummy로 채워줍니다.
이후 위에 페이로드처럼 만들어졌으면, 이를 실행하여 flag를 얻을 수 있습니다.
끝입니다!
'Hacking-기초 > [PWN] Pwnable.xyz' 카테고리의 다른 글
[PWN] xor (50pts) (0) | 2020.08.01 |
---|---|
[PWN] note (50pts) (0) | 2020.07.30 |
[PWN] misalignment (50pts) (0) | 2020.07.28 |
[PWN] add (50pts) (0) | 2020.07.28 |
[PWN] sub (50pts) (0) | 2020.07.28 |