0xd13a

A rookie in a world of pwns

RCTF 2017 Writeup: easyre

easyre

153 points

Please submit the flag like RCTF{flag}.

Download attachment

We are given a Linux application, that asks for input and exits:

1
2
3
4
5
$ ./easy_re

OMG!!!! I forgot kid's id
Ready to exit     
AAAA

After reversing the application in Snowman we see the rough outline:

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
...
pipe(esp1 + 6);
eax3 = fork(esp1 + 6);
esp4 = reinterpret_cast<int32_t*>(esp1 - 1 + 1 - 1 + 1);
if (!eax3) {
    puts("\nOMG!!!! I forgot kid's id", v5, v6);
    write(v7, "69800876143568214356928753", 29);
    puts("Ready to exit     ", "69800876143568214356928753", 29);
    exit(0, "69800876143568214356928753", 29);
    esp4 = esp4 - 1 + 1 - 1 + 1 - 1 + 1 - 1 + 1;
}
read(v8, reinterpret_cast<uint32_t>(esp4) + 46, 29);
esp9 = reinterpret_cast<void*>(esp4 - 1 + 1);
__isoc99_scanf("%d", reinterpret_cast<uint32_t>(esp9) + 36, 29);
esp10 = reinterpret_cast<void*>(reinterpret_cast<uint32_t>(esp9) - 4 + 4);
if (v11 == eax3) {
    eax12 = g80485f7;
    if ((eax12 & 0xff) == 0xcc) {
        puts(":D", reinterpret_cast<uint32_t>(esp9) + 36, 29);
        exit(1, reinterpret_cast<uint32_t>(esp9) + 36, 29);
        esp10 = reinterpret_cast<void*>(reinterpret_cast<uint32_t>(esp10) - 4 + 4 - 4 + 4);
    }
    printf("\nYou got the key\n ", reinterpret_cast<uint32_t>(esp9) + 36, 29);
    lol(reinterpret_cast<uint32_t>(esp10) - 4 + 4 + 46, reinterpret_cast<uint32_t>(esp9) + 36, 29);
}
...

The above code:

  • Opens a pipe for communication
  • Forks
  • The parent application writes to pipe and exits
  • The forked child reads from the pipe and reads a number from keyboard (it expects the entered value to match the PID of the child)
  • Forked child then does some work in lol function:
1
2
3
4
5
6
7
8
9
10
11
12
void lol(void* a1, void* a2, int32_t a3) {
    void* v4;
    int32_t v5;
    int32_t v6;

    if (1) {
        printf("flag_is_not_here", v4, v5);
    } else {
        printf("%s", reinterpret_cast<int32_t>(__zero_stack_offset()) - 4 - 19, v6);
    }
    return;
}

Notice that there is a dead branch in this method that seems to output something. Let’s go to the disassembled code (objdump -d -M intel ./easy_re):

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
080485f4 <lol>:
 80485f4:	55                   	push   ebp
 80485f5:	89 e5                	mov    ebp,esp
 80485f7:	83 ec 28             	sub    esp,0x28
 80485fa:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]  # some interesting calculations happening here
 80485fd:	83 c0 01             	add    eax,0x1
 8048600:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 8048603:	89 c2                	mov    edx,eax
 8048605:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 8048608:	83 c0 01             	add    eax,0x1
 804860b:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 804860e:	8d 04 02             	lea    eax,[edx+eax*1]
 8048611:	88 45 ed             	mov    BYTE PTR [ebp-0x13],al
 8048614:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 8048617:	83 c0 04             	add    eax,0x4
 804861a:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 804861d:	89 c2                	mov    edx,eax
 804861f:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 8048622:	83 c0 05             	add    eax,0x5
 8048625:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 8048628:	8d 04 02             	lea    eax,[edx+eax*1]
 804862b:	88 45 ee             	mov    BYTE PTR [ebp-0x12],al
 804862e:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 8048631:	83 c0 08             	add    eax,0x8
 8048634:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 8048637:	89 c2                	mov    edx,eax
 8048639:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 804863c:	83 c0 09             	add    eax,0x9
 804863f:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 8048642:	8d 04 02             	lea    eax,[edx+eax*1]
 8048645:	88 45 ef             	mov    BYTE PTR [ebp-0x11],al
 8048648:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 804864b:	83 c0 0c             	add    eax,0xc
 804864e:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 8048651:	89 c2                	mov    edx,eax
 8048653:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 8048656:	83 c0 0c             	add    eax,0xc
 8048659:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 804865c:	8d 04 02             	lea    eax,[edx+eax*1]
 804865f:	88 45 f0             	mov    BYTE PTR [ebp-0x10],al
 8048662:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 8048665:	83 c0 12             	add    eax,0x12
 8048668:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 804866b:	89 c2                	mov    edx,eax
 804866d:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 8048670:	83 c0 11             	add    eax,0x11
 8048673:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 8048676:	8d 04 02             	lea    eax,[edx+eax*1]
 8048679:	88 45 f1             	mov    BYTE PTR [ebp-0xf],al
 804867c:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 804867f:	83 c0 0a             	add    eax,0xa
 8048682:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 8048685:	89 c2                	mov    edx,eax
 8048687:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 804868a:	83 c0 15             	add    eax,0x15
 804868d:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 8048690:	8d 04 02             	lea    eax,[edx+eax*1]
 8048693:	88 45 f2             	mov    BYTE PTR [ebp-0xe],al
 8048696:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 8048699:	83 c0 09             	add    eax,0x9
 804869c:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 804869f:	89 c2                	mov    edx,eax
 80486a1:	8b 45 08             	mov    eax,DWORD PTR [ebp+0x8]
 80486a4:	83 c0 19             	add    eax,0x19
 80486a7:	0f b6 00             	movzx  eax,BYTE PTR [eax]
 80486aa:	8d 04 02             	lea    eax,[edx+eax*1]
 80486ad:	88 45 f3             	mov    BYTE PTR [ebp-0xd],al
 80486b0:	c7 45 f4 00 00 00 00 	mov    DWORD PTR [ebp-0xc],0x0   # we set the value to 0
 80486b7:	83 7d f4 01          	cmp    DWORD PTR [ebp-0xc],0x1   # and then compare it to 1
 80486bb:	75 16                	jne    80486d3 <lol+0xdf>        
 80486bd:	b8 c0 88 04 08       	mov    eax,0x80488c0             # this branch will never be taken
 80486c2:	8d 55 ed             	lea    edx,[ebp-0x13]            # and yet it's the one that prints
 80486c5:	89 54 24 04          	mov    DWORD PTR [esp+0x4],edx   #  the value built above
 80486c9:	89 04 24             	mov    DWORD PTR [esp],eax
 80486cc:	e8 ff fd ff ff       	call   80484d0 <printf@plt>
 80486d1:	eb 0d                	jmp    80486e0 <lol+0xec>
 80486d3:	b8 c3 88 04 08       	mov    eax,0x80488c3
 80486d8:	89 04 24             	mov    DWORD PTR [esp],eax
 80486db:	e8 f0 fd ff ff       	call   80484d0 <printf@plt>
 80486e0:	c9                   	leave  
 80486e1:	c3                   	ret    

As it turns out the decompiled C code does not reveal everything, there is a bunch of calculations occurring in that function, and yet the calculated value is abandoned and flag_is_not_here is printed instead.

To reveal that value let’s patch out the executable and replace cmp DWORD PTR [ebp-0xc],0x1 with cmp DWORD PTR [ebp-0xc],0x0. This can be done in any binary editor. Essentially we are replacing 83 7d f4 01 with 83 7d f4 00.

Once the binary is patched we can run it. To figure out the PID we can first run the application, and while it’s waiting for input run ps in a separate shell:

1
2
3
4
$ ps -ef | grep easy
root      1695  1529  0 15:54 pts/0    00:00:00 ./easy_re_patched
root      1696  1695  0 15:54 pts/0    00:00:00 [easy_re_patched] <defunct>
root      1702  1536  0 15:55 pts/1    00:00:00 grep easy

When we enter the PID we get the flag:

1
2
3
4
5
6
7
8
$ ./easy_re_patched 

OMG!!!! I forgot kid's id
Ready to exit     
1696

You got the key
 rhelheg

The flag is RCTF{rhelheg}.