Lleva un tiempo el blog parado, pero traigo algo interesante, veamos como reversear un binario de los presentados en las quals del CTF de la No Con Name.

Aviso: Soy bastante novato en esto, así que seguro que se podrían obviar muchos pasos o hacer más sencillo con los conocimientos y herramientas adecuadas. Si sabes algo de esto, mejor ve ya a la parte curiosa ;).

El binario en cuestión es “inbincible”, si lo ejecutamos produce el siguiente resultado:

1
2
3
$ ./inbincible
Nope!
$

Obviamente no es el que nos interesa, así que veamos lo que hace, abrimos con gdb y buscamos una función desde la que empezar

1
2
3
4
5
$ gdb -q ./inbincible
Reading symbols from ./inbincible...(no debugging symbols found)...done.
(gdb) break main<tab><tab>
main                 main.init            main.main            main.statictmp_0022  main.statictmp_0027
main.func            main.initdone.       main.statictmp_0019  main.statictmp_0024  main.statictmp_0030

Hay un main, así que empecemos por ahí

1
2
3
4
5
6
7
(gdb) break main
Breakpoint 1 at 0x806dc20
(gdb) run
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible

Breakpoint 1, 0x0806dc20 in main ()
(gdb)

Veamos que hay por ahí...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(gdb) disassemble
Dump of assembler code for function main:
=> 0x0806dc20 <+0>:     jmp    0x806b890 <_rt0_go>
   0x0806dc25 <+5>:     add    %al,(%eax)
   0x0806dc27 <+7>:     add    %al,(%eax)
   0x0806dc29 <+9>:     add    %al,(%eax)
   0x0806dc2b <+11>:    add    %al,(%eax)
   0x0806dc2d <+13>:    add    %al,(%eax)
   0x0806dc2f <+15>:    add    %cl,%ch
End of assembler dump.
(gdb)

Parece que sigue por _rt0_go (y nunca vuelve), podemos cambiar el breakpoint a esa función

1
2
3
4
5
6
7
8
(gdb) delete 1
(gdb) break _rt0_go
Breakpoint 2 at 0x806b890
(gdb) continue
Continuing.

Breakpoint 2, 0x0806b890 in _rt0_go ()
(gdb)

Y el código que contiene es:

 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
(gdb) disassemble
Dump of assembler code for function _rt0_go:
=> 0x0806b890 <+0>:     mov    0x4(%esp),%eax
   0x0806b894 <+4>:     mov    0x8(%esp),%ebx
   0x0806b898 <+8>:     sub    $0x80,%esp
   0x0806b89e <+14>:    and    $0xfffffff0,%esp
   0x0806b8a1 <+17>:    mov    %eax,0x78(%esp)
   0x0806b8a5 <+21>:    mov    %ebx,0x7c(%esp)
   0x0806b8a9 <+25>:    mov    $0x814e460,%ebp
   0x0806b8ae <+30>:    lea    -0xff98(%esp),%ebx
   0x0806b8b5 <+37>:    mov    %ebx,0x3c(%ebp)
   0x0806b8b8 <+40>:    mov    %ebx,0x0(%ebp)
   0x0806b8bb <+43>:    mov    %esp,0x4(%ebp)
   0x0806b8be <+46>:    xor    %eax,%eax
   0x0806b8c0 <+48>:    cpuid
   0x0806b8c2 <+50>:    cmp    $0x0,%eax
   0x0806b8c5 <+53>:    je     0x806b8da <_rt0_go+74>
   0x0806b8c7 <+55>:    mov    $0x1,%eax
   0x0806b8cc <+60>:    cpuid
   0x0806b8ce <+62>:    mov    %ecx,0x814e008
   0x0806b8d4 <+68>:    mov    %edx,0x814e00c
   0x0806b8da <+74>:    mov    0x814df28,%eax
   0x0806b8e0 <+80>:    test   %eax,%eax
   0x0806b8e2 <+82>:    je     0x806b905 <_rt0_go+117>
   0x0806b8e4 <+84>:    mov    $0x806cfe0,%ebx

Y sigue hasta +270, pero aceleremos las cosas, no queremos que nos muestre ese mensaje, no? que la función se llame _rt0_go nos indica que es un programa hecho en golang, en ese lenguaje la función para printear es fmt.Print, esperemos por el programa ahí

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(gdb) break fmt.Print
Breakpoint 3 at 0x8071720
(gdb) continue
Continuing.

Breakpoint 3, 0x08071720 in fmt.Print ()
(gdb) bt
#0  0x08071720 in fmt.Print ()
#1  0x08048ebd in main.main ()
(gdb)

Volvemos a empezar, ahora esperando en la dirección que llama a fmt.Print

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(gdb) break *0x08048ebd
Breakpoint 4 at 0x8048ebd
(gdb) delete 2-3
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible
N
Breakpoint 4, 0x08048ebd in main.main ()
(gdb)

Ahora tenemos que buscar que nos lleva ahí para poder evitarlo... el desensamblado es grande, haciendo zoom en la parte donde quedó el programa

  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
 94
 95
 96
 97
 98
 99
100
101
102
(gdb) disassemble
 ...
   0x08048d9d <+413>:   movl   $0x6,0x88(%esp)
   0x08048da8 <+424>:   movl   $0x6,0x8c(%esp)
   0x08048db3 <+435>:   mov    0x814e17c,%ebx
   0x08048db9 <+441>:   cmp    $0x2,%ebx
   0x08048dbc <+444>:   je     0x8048ef1 <main.main+753>
   0x08048dc2 <+450>:   xor    %ecx,%ecx
   0x08048dc4 <+452>:   mov    0x88(%esp),%ebp
   0x08048dcb <+459>:   cmp    %ecx,%ebp
   0x08048dcd <+461>:   jle    0x8048ed1 <main.main+721>
   0x08048dd3 <+467>:   mov    %ecx,%edx
   0x08048dd5 <+469>:   mov    0xa0(%esp),%ebx
   0x08048ddc <+476>:   mov    %ecx,%eax
   0x08048dde <+478>:   cmp    $0xffffffff,%ebx
   0x08048de1 <+481>:   je     0x8048eea <main.main+746>
   0x08048de7 <+487>:   cltd   
   0x08048de8 <+488>:   idiv   %ebx
   0x08048dea <+490>:   mov    %edx,%ebp
   0x08048dec <+492>:   cmp    0xa0(%esp),%ebp
   0x08048df3 <+499>:   jae    0x8048ee3 <main.main+739>
   0x08048df9 <+505>:   mov    0x9c(%esp),%edx
   0x08048e00 <+512>:   lea    (%edx,%ebp,1),%edx
   0x08048e03 <+515>:   movzbl (%edx),%edx
   0x08048e06 <+518>:   mov    %ecx,0x28(%esp)
   0x08048e0a <+522>:   cmp    0x88(%esp),%ecx
   0x08048e11 <+529>:   jae    0x8048edc <main.main+732>
   0x08048e17 <+535>:   mov    0x84(%esp),%ebx
   0x08048e1e <+542>:   lea    (%ebx,%ecx,1),%ebx
   0x08048e21 <+545>:   movzbl (%ebx),%ebx
   0x08048e24 <+548>:   mov    %edx,%ebp
   0x08048e26 <+550>:   xor    %ebx,%ebp
   0x08048e28 <+552>:   xchg   %eax,%ebp
   0x08048e29 <+553>:   movzbl %al,%eax
   0x08048e2c <+556>:   xchg   %eax,%ebp
   0x08048e2d <+557>:   xor    %ecx,%ecx
   0x08048e2f <+559>:   mov    %ebp,(%esp)
   0x08048e32 <+562>:   mov    %ecx,0x4(%esp)
   0x08048e36 <+566>:   call   0x806a6b0 <runtime.intstring>
   0x08048e3b <+571>:   mov    0x8(%esp),%ebx
   0x08048e3f <+575>:   mov    %ebx,0x74(%esp)
   0x08048e43 <+579>:   mov    0xc(%esp),%ebx
   0x08048e47 <+583>:   mov    %ebx,0x78(%esp)
   0x08048e4b <+587>:   lea    0x7c(%esp),%edi
   0x08048e4f <+591>:   xor    %eax,%eax
   0x08048e51 <+593>:   stos   %eax,%es:(%edi)
   0x08048e52 <+594>:   stos   %eax,%es:(%edi)
   0x08048e53 <+595>:   lea    0x7c(%esp),%ebx
   0x08048e57 <+599>:   cmp    $0x0,%ebx
   0x08048e5a <+602>:   je     0x8048ed8 <main.main+728>
   0x08048e5c <+604>:   mov    %ebx,0xa8(%esp)
   0x08048e63 <+611>:   movl   $0x1,0xac(%esp)
   0x08048e6e <+622>:   movl   $0x1,0xb0(%esp)
   0x08048e79 <+633>:   movl   $0x80cc440,(%esp)
   0x08048e80 <+640>:   lea    0x74(%esp),%ebx
   0x08048e84 <+644>:   mov    %ebx,0x4(%esp)
   0x08048e88 <+648>:   call   0x8065ed0 <runtime.convT2E>
   0x08048e8d <+653>:   mov    0xa8(%esp),%edi
   0x08048e94 <+660>:   lea    0x8(%esp),%ebx
   0x08048e98 <+664>:   mov    %ebx,%esi
   0x08048e9a <+666>:   mov    %edi,%edx
   0x08048e9c <+668>:   cld    
   0x08048e9d <+669>:   movsl  %ds:(%esi),%es:(%edi)
   0x08048e9e <+670>:   movsl  %ds:(%esi),%es:(%edi)
   0x08048e9f <+671>:   mov    %edx,(%esp)
   0x08048ea2 <+674>:   mov    0xac(%esp),%ebx
   0x08048ea9 <+681>:   mov    %ebx,0x4(%esp)
   0x08048ead <+685>:   mov    0xb0(%esp),%ebx
   0x08048eb4 <+692>:   mov    %ebx,0x8(%esp)
   0x08048eb8 <+696>:   call   0x8071720 <fmt.Print>
=> 0x08048ebd <+701>:   mov    0x28(%esp),%ecx
   0x08048ec1 <+705>:   inc    %ecx
   0x08048ec2 <+706>:   mov    0x88(%esp),%ebp
   0x08048ec9 <+713>:   cmp    %ecx,%ebp
   0x08048ecb <+715>:   jg     0x8048dd3 <main.main+467>
   0x08048ed1 <+721>:   add    $0xc0,%esp
   0x08048ed7 <+727>:   ret    
   0x08048ed8 <+728>:   mov    %eax,(%ebx)
   0x08048eda <+730>:   jmp    0x8048e5c <main.main+604>
   0x08048edc <+732>:   call   0x8055f40 <runtime.panicindex>
   0x08048ee1 <+737>:   ud2    
   0x08048ee3 <+739>:   call   0x8055f40 <runtime.panicindex>
   0x08048ee8 <+744>:   ud2    
   0x08048eea <+746>:   xor    %ebp,%ebp
   0x08048eec <+748>:   jmp    0x8048dec <main.main+492>
   0x08048ef1 <+753>:   cmpl   $0x1,0x814e17c
   0x08048ef8 <+760>:   jbe    0x804937d <main.main+1917>
   0x08048efe <+766>:   mov    0x814e178,%ebx
   0x08048f04 <+772>:   add    $0x8,%ebx
   0x08048f07 <+775>:   mov    0x4(%ebx),%esi
   0x08048f0a <+778>:   mov    0x6c(%esp),%ebx
   0x08048f0e <+782>:   mov    0x4(%ebx),%ebp
   0x08048f11 <+785>:   cmp    %ebp,%esi
   0x08048f13 <+787>:   je     0x8049048 <main.main+1096>
   0x08048f19 <+793>:   xor    %ecx,%ecx
   0x08048f1b <+795>:   mov    0x88(%esp),%ebp
   0x08048f22 <+802>:   cmp    %ecx,%ebp
   0x08048f24 <+804>:   jle    0x8049028 <main.main+1064>

---Type <return> to continue, or q <return> to quit---q
Quit
(gdb)

Podemos ver que por lo menos hasta +467 es parte del bucle, ¿que jump se pudo tomar o evitar antes?

1
2
3
4
5
6
7
8
9
   0x08048d9d <+413>:   movl   $0x6,0x88(%esp)
   0x08048da8 <+424>:   movl   $0x6,0x8c(%esp)
   0x08048db3 <+435>:   mov    0x814e17c,%ebx
   0x08048db9 <+441>:   cmp    $0x2,%ebx
   0x08048dbc <+444>:   je     0x8048ef1 <main.main+753>
   0x08048dc2 <+450>:   xor    %ecx,%ecx
   0x08048dc4 <+452>:   mov    0x88(%esp),%ebp
   0x08048dcb <+459>:   cmp    %ecx,%ebp
   0x08048dcd <+461>:   jle    0x8048ed1 <main.main+721>

El más cercano, que nos lleva a +721 provocará un ret, que no es lo que queremos

1
2
   0x08048ed1 <+721>:   add    $0xc0,%esp
   0x08048ed7 <+727>:   ret

El siguiente, un je nos lleva a +753 que parece que tiene más recorrido, veamos el cmp que lo precede

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(gdb) break *0x08048db9
Breakpoint 5 at 0x8048db9
(gdb) delete 4
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible

Breakpoint 5, 0x08048db9 in main.main ()
(gdb) x/i $eip
=> 0x8048db9 <main.main+441>:   cmp    $0x2,%ebx
(gdb)

Se supone que debería dar true, y el valor de %ebx es...

1
2
3
(gdb) print $ebx
$1 = 1
(gdb)

Así que no, ahora, ¿cúal es el 1 que podemos haber metido en el programa?

¿El número de parámetros?

1
2
3
4
5
6
7
8
9
(gdb) run test
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible test

Breakpoint 5, 0x08048db9 in main.main ()
(gdb) print $ebx
$2 = 2
(gdb)

Parece que sí, por supuesto no es suficiente :P

1
2
3
4
5
(gdb) continue
Continuing.
Nope!
[Inferior 1 (process 16416) exited normally]
(gdb)

Repetimos...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
(gdb) delete 5
(gdb) break fmt.Print
Breakpoint 6 at 0x8071720
(gdb) run
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible test

Breakpoint 6, 0x08071720 in fmt.Print ()
(gdb) bt
#0  0x08071720 in fmt.Print ()
#1  0x08049014 in main.main ()
(gdb) break *0x08049014
Breakpoint 7 at 0x8049014
(gdb) delete 6
(gdb) run 
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible test
N
Breakpoint 7, 0x08049014 in main.main ()
(gdb)

Ahora nos quedamos aquí

 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
(gdb) disassemble
 ...
   0x08048f07 <+775>:   mov    0x4(%ebx),%esi
   0x08048f0a <+778>:   mov    0x6c(%esp),%ebx
   0x08048f0e <+782>:   mov    0x4(%ebx),%ebp
   0x08048f11 <+785>:   cmp    %ebp,%esi
   0x08048f13 <+787>:   je     0x8049048 <main.main+1096>
   0x08048f19 <+793>:   xor    %ecx,%ecx
   0x08048f1b <+795>:   mov    0x88(%esp),%ebp
   0x08048f22 <+802>:   cmp    %ecx,%ebp
   0x08048f24 <+804>:   jle    0x8049028 <main.main+1064>
   0x08048f2a <+810>:   mov    %ecx,%edx
   0x08048f2c <+812>:   mov    0xa0(%esp),%ebx
   0x08048f33 <+819>:   mov    %ecx,%eax
   0x08048f35 <+821>:   cmp    $0xffffffff,%ebx
   0x08048f38 <+824>:   je     0x8049041 <main.main+1089>
   0x08048f3e <+830>:   cltd   
   0x08048f3f <+831>:   idiv   %ebx
   0x08048f41 <+833>:   mov    %edx,%ebp
   0x08048f43 <+835>:   cmp    0xa0(%esp),%ebp
   0x08048f4a <+842>:   jae    0x804903a <main.main+1082>
   0x08048f50 <+848>:   mov    0x9c(%esp),%edx
   0x08048f57 <+855>:   lea    (%edx,%ebp,1),%edx
   0x08048f5a <+858>:   movzbl (%edx),%edx
   0x08048f5d <+861>:   mov    %ecx,0x1c(%esp)
   0x08048f61 <+865>:   cmp    0x88(%esp),%ecx
   0x08048f68 <+872>:   jae    0x8049033 <main.main+1075>
   0x08048f6e <+878>:   mov    0x84(%esp),%ebx
   0x08048f75 <+885>:   lea    (%ebx,%ecx,1),%ebx
   0x08048f78 <+888>:   movzbl (%ebx),%ebx
   0x08048f7b <+891>:   mov    %edx,%ebp
   0x08048f7d <+893>:   xor    %ebx,%ebp
   0x08048f7f <+895>:   xchg   %eax,%ebp
   0x08048f80 <+896>:   movzbl %al,%eax
   0x08048f83 <+899>:   xchg   %eax,%ebp
   0x08048f84 <+900>:   xor    %ecx,%ecx
   0x08048f86 <+902>:   mov    %ebp,(%esp)
   0x08048f89 <+905>:   mov    %ecx,0x4(%esp)
   0x08048f8d <+909>:   call   0x806a6b0 <runtime.intstring>
   0x08048f92 <+914>:   mov    0x8(%esp),%ebx
   0x08048f96 <+918>:   mov    %ebx,0x74(%esp)
   0x08048f9a <+922>:   mov    0xc(%esp),%ebx
   0x08048f9e <+926>:   mov    %ebx,0x78(%esp)
   0x08048fa2 <+930>:   lea    0x7c(%esp),%edi
   0x08048fa6 <+934>:   xor    %eax,%eax
   0x08048fa8 <+936>:   stos   %eax,%es:(%edi)
   0x08048fa9 <+937>:   stos   %eax,%es:(%edi)
   0x08048faa <+938>:   lea    0x7c(%esp),%ebx
   0x08048fae <+942>:   cmp    $0x0,%ebx
   0x08048fb1 <+945>:   je     0x804902f <main.main+1071>
   0x08048fb3 <+947>:   mov    %ebx,0xa8(%esp)
   0x08048fba <+954>:   movl   $0x1,0xac(%esp)
   0x08048fc5 <+965>:   movl   $0x1,0xb0(%esp)
   0x08048fd0 <+976>:   movl   $0x80cc440,(%esp)
   0x08048fd7 <+983>:   lea    0x74(%esp),%ebx
   0x08048fdb <+987>:   mov    %ebx,0x4(%esp)
   0x08048fdf <+991>:   call   0x8065ed0 <runtime.convT2E>
   0x08048fe4 <+996>:   mov    0xa8(%esp),%edi
   0x08048feb <+1003>:  lea    0x8(%esp),%ebx
   0x08048fef <+1007>:  mov    %ebx,%esi
   0x08048ff1 <+1009>:  mov    %edi,%edx
   0x08048ff3 <+1011>:  cld    
   0x08048ff4 <+1012>:  movsl  %ds:(%esi),%es:(%edi)
   0x08048ff5 <+1013>:  movsl  %ds:(%esi),%es:(%edi)
   0x08048ff6 <+1014>:  mov    %edx,(%esp)
   0x08048ff9 <+1017>:  mov    0xac(%esp),%ebx
   0x08049000 <+1024>:  mov    %ebx,0x4(%esp)
   0x08049004 <+1028>:  mov    0xb0(%esp),%ebx
   0x0804900b <+1035>:  mov    %ebx,0x8(%esp)
   0x0804900f <+1039>:  call   0x8071720 <fmt.Print>
=> 0x08049014 <+1044>:  mov    0x1c(%esp),%ecx
   0x08049018 <+1048>:  inc    %ecx
   0x08049019 <+1049>:  mov    0x88(%esp),%ebp
   0x08049020 <+1056>:  cmp    %ecx,%ebp
   0x08049022 <+1058>:  jg     0x8048f2a <main.main+810>
   0x08049028 <+1064>:  add    $0xc0,%esp
   0x0804902e <+1070>:  ret    
   0x0804902f <+1071>:  mov    %eax,(%ebx)
   0x08049031 <+1073>:  jmp    0x8048fb3 <main.main+947>
   0x08049033 <+1075>:  call   0x8055f40 <runtime.panicindex>
   0x08049038 <+1080>:  ud2    
   0x0804903a <+1082>:  call   0x8055f40 <runtime.panicindex>
   0x0804903f <+1087>:  ud2    
   0x08049041 <+1089>:  xor    %ebp,%ebp
   0x08049043 <+1091>:  jmp    0x8048f43 <main.main+835>
   0x08049048 <+1096>:  movl   $0x80ca7c0,(%esp)
   0x0804904f <+1103>:  call   0x80681a0 <runtime.new>
   0x08049054 <+1108>:  mov    0x4(%esp),%ebx
Quit
(gdb)

De nuevo otro bucle, siguiendo la misma estrategia buscaremos el jump anterior a el bucle que nos sacaría de el, obviando el primero que nos lleva a un ret, el siguente es

1
2
   0x08048f11 <+785>:   cmp    %ebp,%esi
   0x08048f13 <+787>:   je     0x8049048 <main.main+1096>

Vamos allá (no nos olvidemos del parámetro)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(gdb) break *0x08048f11
Breakpoint 8 at 0x8048f11
(gdb) delete 7
(gdb) run test
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible test

Breakpoint 8, 0x08048f11 in main.main ()
(gdb) x/i $eip
=> 0x8048f11 <main.main+785>:   cmp    %ebp,%esi
(gdb)

Los valores son...

1
2
3
4
5
(gdb) print $ebp
$3 = (void *) 0x10
(gdb) print $esi
$4 = 4
(gdb)

Un 0x10 y un 4, y la comprobación anterior era el número de parámetros, y el que habíamos probado tiene 4 caracteres, ¿uno de 16 será la solución?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(gdb) run testtesttesttest
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible testtesttesttest

Breakpoint 8, 0x08048f11 in main.main ()
(gdb) print $ebp
$7 = (void *) 0x10
(gdb) print $esi
$8 = 16
(gdb)

Toma un parámetro de 16 caracteres, vale, repetimos

 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
(gdb) delete 8
(gdb) continue
Continuing.
Nope!
[Inferior 1 (process 20465) exited normally]
(gdb) break fmt.Print
Breakpoint 9 at 0x8071720
(gdb) run testtesttesttest
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible testtesttesttest
[New LWP 20722]
[Switching to LWP 20722]

Breakpoint 9, 0x08071720 in fmt.Print ()
(gdb) bt
#0  0x08071720 in fmt.Print ()
#1  0x0804935a in main.main ()
(gdb) break *0x0804935a
Breakpoint 10 at 0x804935a
(gdb) delete 9
(gdb) run testtesttesttest
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kenkeiras/ctf/2014/ncn/quals/inbincible/inbincible testtesttesttest
N[New LWP 20800]
[Switching to LWP 20800]

Breakpoint 10, 0x0804935a in main.main ()
(gdb)

¿LWP?, ¡threads!, parece que la cosa se complica, pero busquemos la condición

 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
 ...
   0x0804924e <+1614>:  call   0x8055f40 <runtime.panicindex>
   0x08049253 <+1619>:  ud2    
   0x08049255 <+1621>:  call   0x8055f40 <runtime.panicindex>
   0x0804925a <+1626>:  ud2    
   0x0804925c <+1628>:  xor    %ebp,%ebp
   0x0804925e <+1630>:  jmp    0x804915e <main.main+1374>
   0x08049263 <+1635>:  xor    %ecx,%ecx
   0x08049265 <+1637>:  mov    0x88(%esp),%ebp
   0x0804926c <+1644>:  cmp    %ecx,%ebp
   0x0804926e <+1646>:  jle    0x8049243 <main.main+1603>
   0x08049270 <+1648>:  mov    %ecx,%edx
   0x08049272 <+1650>:  mov    0xa0(%esp),%ebx
   0x08049279 <+1657>:  mov    %ecx,%eax
   0x0804927b <+1659>:  cmp    $0xffffffff,%ebx
   0x0804927e <+1662>:  je     0x8049376 <main.main+1910>
   0x08049284 <+1668>:  cltd   
   0x08049285 <+1669>:  idiv   %ebx
   0x08049287 <+1671>:  mov    %edx,%ebp
   0x08049289 <+1673>:  cmp    0xa0(%esp),%ebp
   0x08049290 <+1680>:  jae    0x804936f <main.main+1903>
   0x08049296 <+1686>:  mov    0x9c(%esp),%edx
   0x0804929d <+1693>:  lea    (%edx,%ebp,1),%edx
   0x080492a0 <+1696>:  movzbl (%edx),%edx
   0x080492a3 <+1699>:  mov    %ecx,0x2c(%esp)
   0x080492a7 <+1703>:  cmp    0x88(%esp),%ecx
   0x080492ae <+1710>:  jae    0x8049368 <main.main+1896>
   0x080492b4 <+1716>:  mov    0x84(%esp),%ebx
   0x080492bb <+1723>:  lea    (%ebx,%ecx,1),%ebx
   0x080492be <+1726>:  movzbl (%ebx),%ebx
   0x080492c1 <+1729>:  mov    %edx,%ebp
   0x080492c3 <+1731>:  xor    %ebx,%ebp
   0x080492c5 <+1733>:  xchg   %eax,%ebp
   0x080492c6 <+1734>:  movzbl %al,%eax
   0x080492c9 <+1737>:  xchg   %eax,%ebp
   0x080492ca <+1738>:  xor    %ecx,%ecx
   0x080492cc <+1740>:  mov    %ebp,(%esp)
   0x080492cf <+1743>:  mov    %ecx,0x4(%esp)
   0x080492d3 <+1747>:  call   0x806a6b0 <runtime.intstring>
   0x080492d8 <+1752>:  mov    0x8(%esp),%ebx
   0x080492dc <+1756>:  mov    %ebx,0x74(%esp)
   0x080492e0 <+1760>:  mov    0xc(%esp),%ebx
   0x080492e4 <+1764>:  mov    %ebx,0x78(%esp)
   0x080492e8 <+1768>:  lea    0x7c(%esp),%edi
   0x080492ec <+1772>:  xor    %eax,%eax
   0x080492ee <+1774>:  stos   %eax,%es:(%edi)
   0x080492ef <+1775>:  stos   %eax,%es:(%edi)
   0x080492f0 <+1776>:  lea    0x7c(%esp),%ebx
   0x080492f4 <+1780>:  cmp    $0x0,%ebx
   0x080492f7 <+1783>:  je     0x8049364 <main.main+1892>
   0x080492f9 <+1785>:  mov    %ebx,0xa8(%esp)
   0x08049300 <+1792>:  movl   $0x1,0xac(%esp)
   0x0804930b <+1803>:  movl   $0x1,0xb0(%esp)
   0x08049316 <+1814>:  movl   $0x80cc440,(%esp)
   0x0804931d <+1821>:  lea    0x74(%esp),%ebx
   0x08049321 <+1825>:  mov    %ebx,0x4(%esp)
   0x08049325 <+1829>:  call   0x8065ed0 <runtime.convT2E>
   0x0804932a <+1834>:  mov    0xa8(%esp),%edi
   0x08049331 <+1841>:  lea    0x8(%esp),%ebx
   0x08049335 <+1845>:  mov    %ebx,%esi
   0x08049337 <+1847>:  mov    %edi,%edx
   0x08049339 <+1849>:  cld    
   0x0804933a <+1850>:  movsl  %ds:(%esi),%es:(%edi)
   0x0804933b <+1851>:  movsl  %ds:(%esi),%es:(%edi)
   0x0804933c <+1852>:  mov    %edx,(%esp)
   0x0804933f <+1855>:  mov    0xac(%esp),%ebx
   0x08049346 <+1862>:  mov    %ebx,0x4(%esp)
   0x0804934a <+1866>:  mov    0xb0(%esp),%ebx
   0x08049351 <+1873>:  mov    %ebx,0x8(%esp)
   0x08049355 <+1877>:  call   0x8071720 <fmt.Print>
=> 0x0804935a <+1882>:  mov    0x2c(%esp),%ecx
   0x0804935e <+1886>:  inc    %ecx
   0x0804935f <+1887>:  jmp    0x8049265 <main.main+1637>
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb)

El bucle llega hasta +1637, pero hay algun jump incondicional y alguna llamada nada prometedora a <runtime.panicindex>, así que esta vez habrá que buscar el jump que nos lleva aquí (por lo menos hasta +1635) y negarlo, en vez de al revés.

Podemos encontrarlo en +1326

1
2
   0x0804912b <+1323>:  cmp    $0x0,%cl
   0x0804912e <+1326>:  je     0x8049263 <main.main+1635>

Ahí encontramos el bucle que nos interesa

 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
 ...
   0x08049084 <+1156>:  mov    0x6c(%esp),%ebx
   0x08049088 <+1160>:  mov    0x4(%ebx),%ebp
   0x0804908b <+1163>:  mov    %esi,0x24(%esp)
   0x0804908f <+1167>:  cmp    %esi,%ebp
   0x08049091 <+1169>:  jle    0x80490d6 <main.main+1238>
   0x08049093 <+1171>:  movl   $0x80dfa60,(%esp)
   0x0804909a <+1178>:  call   0x80681a0 <runtime.new>
   0x0804909f <+1183>:  mov    0x4(%esp),%eax
   0x080490a3 <+1187>:  movl   $0x80493c0,(%eax)
   0x080490a9 <+1193>:  mov    0x68(%esp),%edx
   0x080490ad <+1197>:  mov    %edx,0x4(%eax)
   0x080490b0 <+1200>:  mov    0x6c(%esp),%edx
   0x080490b4 <+1204>:  mov    %edx,0x8(%eax)
   0x080490b7 <+1207>:  mov    0x70(%esp),%edx
   0x080490bb <+1211>:  mov    %edx,0xc(%eax)
   0x080490be <+1214>:  mov    0x24(%esp),%ebx
   0x080490c2 <+1218>:  mov    %ebx,(%esp)
   0x080490c5 <+1221>:  push   %eax
   0x080490c6 <+1222>:  push   $0x4
   0x080490c5 <+1221>:  push   %eax
   0x080490c6 <+1222>:  push   $0x4
   0x080490c8 <+1224>:  call   0x805acf0 <runtime.newproc>
   0x080490cd <+1229>:  pop    %ecx
   0x080490ce <+1230>:  pop    %ecx
   0x080490cf <+1231>:  mov    0x24(%esp),%esi
   0x080490d3 <+1235>:  inc    %esi
   0x080490d4 <+1236>:  jmp    0x8049084 <main.main+1156>
   0x080490d6 <+1238>:  mov    $0x1,%ecx
   0x080490db <+1243>:  xor    %edi,%edi
   0x080490dd <+1245>:  mov    0x6c(%esp),%ebx
   0x080490e1 <+1249>:  mov    0x4(%ebx),%ebp
   0x080490e4 <+1252>:  mov    %edi,0x20(%esp)
   0x080490e8 <+1256>:  cmp    %edi,%ebp
   0x080490ea <+1258>:  jle    0x804912b <main.main+1323>
   0x080490ec <+1260>:  cmp    $0x0,%cl
   0x080490ef <+1263>:  je     0x8049127 <main.main+1319>
   0x080490f1 <+1265>:  movb   $0x0,0x1b(%esp)
   0x080490f6 <+1270>:  movl   $0x80ca7c0,(%esp)
   0x080490fd <+1277>:  mov    0x68(%esp),%ebx
   0x08049101 <+1281>:  mov    (%ebx),%ebp
   0x08049103 <+1283>:  mov    %ebp,0x4(%esp)
   0x08049107 <+1287>:  lea    0x1b(%esp),%ebx
   0x0804910b <+1291>:  mov    %ebx,0x8(%esp)
   0x0804910f <+1295>:  call   0x8063620 <runtime.chanrecv1>
   0x08049114 <+1300>:  mov    0x20(%esp),%edi
   0x08049118 <+1304>:  cmpb   $0x0,0x1b(%esp)
   0x0804911d <+1309>:  je     0x8049127 <main.main+1319>
   0x0804911f <+1311>:  mov    $0x1,%ecx
   0x08049124 <+1316>:  inc    %edi
   0x08049125 <+1317>:  jmp    0x80490dd <main.main+1245>
   0x08049127 <+1319>:  xor    %ecx,%ecx
   0x08049129 <+1321>:  jmp    0x8049124 <main.main+1316>
-- 0x0804912b <+1323>:  cmp    $0x0,%cl
   0x0804912e <+1326>:  je     0x8049263 <main.main+1635>
   0x08049134 <+1332>:  xor    %ecx,%ecx
   0x08049136 <+1334>:  mov    0x94(%esp),%ebp
   0x0804913d <+1341>:  cmp    %ecx,%ebp
   0x0804913f <+1343>:  jle    0x8049243 <main.main+1603>
   0x08049145 <+1349>:  mov    %ecx,%edx
   0x08049147 <+1351>:  mov    0xa0(%esp),%ebx
   0x0804914e <+1358>:  mov    %ecx,%eax
   0x08049150 <+1360>:  cmp    $0xffffffff,%ebx
   0x08049153 <+1363>:  je     0x804925c <main.main+1628>

La línea indicada es la de la comparación, parece que tenemos que mantener %cl a algo que no sea 0.

A ese bucle se llega a través del salto a +1238 que se puede ver al principio del listado, esa instrucción es

1
       0x080490d6 <+1238>:  mov    $0x1,%ecx

Así que al principio %ecx es 1, alguna instrucción lo pone a 0, una es

1
2
   0x08049127 <+1319>:  xor    %ecx,%ecx
   0x08049129 <+1321>:  jmp    0x8049124 <main.main+1316>

Y llega ahí desde

1
2
3
4
   0x0804910f <+1295>:  call   0x8063620 <runtime.chanrecv1>
   0x08049114 <+1300>:  mov    0x20(%esp),%edi
   0x08049118 <+1304>:  cmpb   $0x0,0x1b(%esp)
   0x0804911d <+1309>:  je     0x8049127 <main.main+1319>

La trama se complica

El salto es dependiente de lo que parece el resultado de lo que se recibe por un canal que se deja en 0x1b(%esp), y yo lo siento, no se debuggear hilos que se interconectan y cosas así :(

Pero hagamos un último esfuerzo, eso es un bucle, con un poco de suerte hará una pasada por cada letra... con un poco de suerte dirá si cada letra es correcta o no y podamos ir resolviendolo haciendo fuerza bruta letra a letra, algo que es bastante más asequible que atacar 16 (256**16 posibilidades en el peor de los casos).

¿O no suena mejor buscar uno entre 4096 que uno entre 340282366920938463463374607431768211456?

Por suerte gdb se puede automatizar facilmente, por ejemplo, si ponemos esto en un archivo

break *0x08049118
run testtesttesttest
x/1c ($esp) + 0x1b

Y le decimos a gdb que lo ejecute...

1
2
3
4
5
6
7
8
$ gdb -batch -x man ./inbincible
Breakpoint 1 at 0x8049118
[New LWP 30150]
[Switching to LWP 30150]

Breakpoint 1, 0x08049118 in main.main ()
0xf7edeef3:     0 '\000'
$

Tachán! podemos ver que pasa en la memoria del programa, veamos si podemos scriptearlo para que pruebe todas las letras, primero tenemos que generar los archivos de control para cada password y añadiremos unos cuantos “continues” para que siga después del primer resultado

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CONTROLLER_NAME = 'manipulator'

def gen_ctrl(passwd):
    f = open(CONTROLLER_NAME, 'w')

    # Breakpoint
    f.write("break *0x08049118\n")

    f.write("run %s\n" % passwd)
    for i in xrange(128):
        f.write("x/1c ($esp) + 0x1b\n")
        f.write("continue\n")
    f.close()

Lo siguiente será lanzar el gdb, ya aprovechamos que estamos en python para hacer el tratamiento que haga falta

 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
PROGNAME = 'inbincible'

import subprocess

def test(passwd):
    # Generamos el archivo de control
    gen_ctrl(passwd)

    # Lanzamos el gdb y tomamos control de su stdout y stderr
    p = subprocess.Popen(["gdb", PROGNAME, '-batch', '-x', CONTROLLER_NAME],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)
    print passwd

    # Buscaremos en el stdout las líneas con el dump de la memoria
    # que son las que siguen al aviso de que se llegó a un breakpoint
    o = p.stdout.read()
    nxt = False
    for l in o.split('\n'):

        if nxt:
            print l
            nxt = False

        if 'Breakpoint 1, 0x08049118 in main.main ()' in l:
            nxt = True

Bien, eso debería mostrar las líneas con la información que nos interesa por ahora veamos que tal va y ya parsearemos y tal luego

1
2
3
4
5
import string

CHARS = string.printable.strip()
for c in CHARS:
    test(c * 16)

A ver, ¿algo dará 1 o toca debuggear hilos?

  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
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
$ python test.py 
aaaaaaaaaaaaaaaa
0xf7edeef3:     0 '\000'
bbbbbbbbbbbbbbbb
0xf7edeef3:     0 '\000'
cccccccccccccccc
0xf7edeef3:     0 '\000'
dddddddddddddddd
0xf7edeef3:     0 '\000'
eeeeeeeeeeeeeeee
0xf7edeef3:     0 '\000'
ffffffffffffffff
0xf7edeef3:     0 '\000'
gggggggggggggggg
0xf7edeef3:     0 '\000'
hhhhhhhhhhhhhhhh
0xf7edeef3:     0 '\000'
iiiiiiiiiiiiiiii
0xf7edeef3:     0 '\000'
jjjjjjjjjjjjjjjj
0xf7edeef3:     0 '\000'
kkkkkkkkkkkkkkkk
0xf7edeef3:     0 '\000'
llllllllllllllll
0xf7edeef3:     0 '\000'
mmmmmmmmmmmmmmmm
0xf7edeef3:     0 '\000'
nnnnnnnnnnnnnnnn
0xf7edeef3:     0 '\000'
oooooooooooooooo
0xf7edeef3:     0 '\000'
pppppppppppppppp
0xf7edeef3:     0 '\000'
qqqqqqqqqqqqqqqq
0xf7edeef3:     0 '\000'
rrrrrrrrrrrrrrrr
0xf7edeef3:     0 '\000'
ssssssssssssssss
0xf7edeef3:     0 '\000'
tttttttttttttttt
0xf7edeef3:     0 '\000'
uuuuuuuuuuuuuuuu
0xf7edeef3:     0 '\000'
vvvvvvvvvvvvvvvv
0xf7edeef3:     0 '\000'
wwwwwwwwwwwwwwww
0xf7edeef3:     0 '\000'
xxxxxxxxxxxxxxxx
0xf7edeef3:     0 '\000'
yyyyyyyyyyyyyyyy
0xf7edeef3:     0 '\000'
zzzzzzzzzzzzzzzz
0xf7edeef3:     0 '\000'
AAAAAAAAAAAAAAAA
0xf7edeef3:     0 '\000'
BBBBBBBBBBBBBBBB
0xf7edeef3:     0 '\000'
CCCCCCCCCCCCCCCC
0xf7edeef3:     0 '\000'
DDDDDDDDDDDDDDDD
0xf7edeef3:     0 '\000'
EEEEEEEEEEEEEEEE
0xf7edeef3:     0 '\000'
FFFFFFFFFFFFFFFF
0xf7edeef3:     0 '\000'
GGGGGGGGGGGGGGGG
0xf7edeef3:     1 '\001'
0xf7edeef3:     0 '\000'
HHHHHHHHHHHHHHHH
0xf7edeef3:     0 '\000'
IIIIIIIIIIIIIIII
0xf7edeef3:     0 '\000'
JJJJJJJJJJJJJJJJ
0xf7edeef3:     0 '\000'
KKKKKKKKKKKKKKKK
0xf7edeef3:     0 '\000'
LLLLLLLLLLLLLLLL
0xf7edeef3:     0 '\000'
MMMMMMMMMMMMMMMM
0xf7edeef3:     0 '\000'
NNNNNNNNNNNNNNNN
0xf7edeef3:     0 '\000'
OOOOOOOOOOOOOOOO
0xf7edeef3:     0 '\000'
PPPPPPPPPPPPPPPP
0xf7edeef3:     0 '\000'
QQQQQQQQQQQQQQQQ
0xf7edeef3:     0 '\000'
RRRRRRRRRRRRRRRR
0xf7edeef3:     0 '\000'
SSSSSSSSSSSSSSSS
0xf7edeef3:     0 '\000'
TTTTTTTTTTTTTTTT
0xf7edeef3:     0 '\000'
UUUUUUUUUUUUUUUU
0xf7edeef3:     0 '\000'
VVVVVVVVVVVVVVVV
0xf7edeef3:     0 '\000'
WWWWWWWWWWWWWWWW
0xf7edeef3:     0 '\000'
XXXXXXXXXXXXXXXX
0xf7edeef3:     0 '\000'
YYYYYYYYYYYYYYYY
0xf7edeef3:     0 '\000'
ZZZZZZZZZZZZZZZZ
 ...

¡Sí!

1
2
3
GGGGGGGGGGGGGGGG
0xf7edeef3:     1 '\001'
0xf7edeef3:     0 '\000'

Y no hace falta ni parsear si sigue pasando por ahí es que vamos bien, podemos simplemente contar las veces que se pasa por el breakpoint :D, hacemos un par de arreglos, en test()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def test(passwd):
    # Generamos el archivo de control
    gen_ctrl(passwd)

    # Lanzamos el gdb y tomamos control de su stdout y stderr
    p = subprocess.Popen(["gdb", PROGNAME, '-batch', '-x', CONTROLLER_NAME],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)

    # Buscaremos en el stdout las líneas con el dump de la memoria
    # que son las que siguen al aviso de que se llegó a un breakpoint
    o = p.stdout.read()
    nxt = False
    i = -1
    for l in o.split('\n'):

        if nxt:
            i += 1
            nxt = False

        if 'Breakpoint 1, 0x08049118 in main.main ()' in l:
            nxt = True

    return i

En el bucle principal

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CHARS = string.printable.strip()
best = None
top = 0
for c in CHARS:
    result = test(c * 16)
    if result > top:
        best = c
        top = result

print "Best:", best
print "With" , top, "matches"

El resultado es el que habíamos encontrado antes, claro, la primera letra es:

$ python test.py 
Best: G
With 1 matches
$

Ahora podemos hacer que busque el resto, si repetimos ese bucle para cada letra guardando las anteriores...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
matches = []
for i in xrange(16):
    best = None
    top = 0
    for c in CHARS:
        result = test(''.join(matches) + c * (16 - len(matches)))
        if result > top:
            best = c
            top = result

    #                                    ↓decoración, eliminar si se ve mal
    print "Current:", ''.join(matches) + '\x1b[1m' + best + '\x1b[0m'
    matches.append(best)
    print "Best:", best
    print "With" , top, "matches"

print "Resultado: ", ''.join(matches)

Y ahí va...

 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
$ python test.py 
Current: G
Best: G
With 1 matches
Current: G0
Best: 0
With 2 matches
Current: G0w
Best: w
With 3 matches
Current: G0w1
Best: 1
With 4 matches
Current: G0w1n
Best: n
With 5 matches
Current: G0w1n!
Best: !
With 6 matches
Current: G0w1n!C
Best: C
With 7 matches
Current: G0w1n!C0
Best: 0
With 8 matches
Current: G0w1n!C0n
Best: n
With 9 matches
Current: G0w1n!C0ng
Best: g
With 10 matches
Current: G0w1n!C0ngr
Best: r
With 11 matches
Current: G0w1n!C0ngr4
Best: 4
With 12 matches
Current: G0w1n!C0ngr4t
Best: t
With 13 matches
Current: G0w1n!C0ngr4t5
Best: 5
With 14 matches
Current: G0w1n!C0ngr4t5!
Best: !
With 15 matches
Current: G0w1n!C0ngr4t5!0
Best: 0
With 15 matches
Resultado:  G0w1n!C0ngr4t5!0
$

“G0w1n!C0ngr4t5!0”, lo probamos y ...

1
2
3
$ ./inbincible 'G0w1n!C0ngr4t5!0'
Nope!
$

¿¡Qué!?

Bien, resulta que el último caracter no lo saca bien por que da igual para el número de iteraciones (lo que tiene ahorrarse medio minuto contando líneas en vez de buscar unos :P), si hacemos el bucle como debiera ser...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
i = 0
for l in o.split('\n'):

    if nxt:
        if '\\001' in l:
            i += 1
        nxt = False

    if 'Breakpoint 1, 0x08049118 in main.main ()' in l:
        nxt = True

return i

Acabamos con el resultado real

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ python test.py
Current: G
Best: G
With 1 matches
 ...
Current: G0w1n!C0ngr4t5!!
Best: !
With 16 matches
Resultado:  G0w1n!C0ngr4t5!!
$

Y ahora sí

1
2
3
$ ./inbincible 'G0w1n!C0ngr4t5!!'
Yeah!
$