Skip to content

Instantly share code, notes, and snippets.

@yifanlu
Created August 6, 2012 21:57
Show Gist options
  • Save yifanlu/3278820 to your computer and use it in GitHub Desktop.
Save yifanlu/3278820 to your computer and use it in GitHub Desktop.
Vita possible exploit testing
//
// main.c
//
// Created by Yifan Lu on 8/6/12.
//
#define G_DIR_SEPARATOR '/'
// I don't know if __native_client__ is defined on the Vita or not, so
// you can try it both ways.
#define __native_client__
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
gchar *
gg_path_get_basename (const char *filename)
{
char *r;
g_return_val_if_fail (filename != NULL, NULL);
/* Empty filename -> . */
if (!*filename)
return g_strdup (".");
/* No separator -> filename */
r = strrchr (filename, G_DIR_SEPARATOR);\
if (r == NULL)
return g_strdup (filename);
/* Trailing slash, remove component */
if (r [1] == 0){
char *copy = g_strdup (filename);
copy [r-filename] = 0;
r = strrchr (copy, G_DIR_SEPARATOR);
if (r == NULL){
g_free (copy);
return g_strdup ("/");
}
r = g_strdup (&r[1]);
g_free (copy);
return r;
}
return g_strdup (&r[1]);
}
gchar *
mono_path_canonicalize (const char *path)
{
gchar *abspath, *pos, *lastpos, *dest;
int backc;
if (g_path_is_absolute (path)) {
abspath = g_strdup (path);
} else {
#ifdef __native_client__
gchar *tmpdir = ".";
abspath = g_build_filename (tmpdir, path, NULL);
#else
gchar *tmpdir = g_get_current_dir ();
abspath = g_build_filename (tmpdir, path, NULL);
g_free (tmpdir);
#endif
}
abspath = g_strreverse (abspath);
backc = 0;
dest = lastpos = abspath;
pos = strchr (lastpos, G_DIR_SEPARATOR);
while (pos != NULL) {
int len = pos - lastpos;
if (len == 1 && lastpos [0] == '.') {
// nop
} else if (len == 2 && lastpos [0] == '.' && lastpos [1] == '.') {
backc++;
} else if (len > 0) {
if (backc > 0) {
backc--;
} else {
if (dest != lastpos)
/* The two strings can overlap */
memmove (dest, lastpos, len + 1);
dest += len + 1;
}
}
lastpos = pos + 1;
pos = strchr (lastpos, G_DIR_SEPARATOR);
}
if (dest != lastpos) strcpy (dest, lastpos);
return g_strreverse (abspath);
}
char *
make_vita_path (const char *path)
{
gchar *abspath, *pos, *lastpos, *dest;
int backc;
abspath = strdup (path);
int i;
for(i = 0; i < strlen(abspath); i++)
if(abspath[i] == '\\')
abspath[i] = '/';
abspath = g_strreverse (abspath);
backc = 0;
dest = lastpos = abspath;
pos = strchr (lastpos, G_DIR_SEPARATOR);
while (pos != NULL) {
int len = pos - lastpos;
if (len == 1 && lastpos [0] == '.') {
// nop
} else if (len == 2 && lastpos [0] == '.' && lastpos [1] == '.') {
backc++;
} else if (len > 0) {
if (backc > 0) {
backc--;
} else {
if (dest != lastpos)
/* The two strings can overlap */
memmove (dest, lastpos, len + 1);
dest += len + 1;
}
}
lastpos = pos + 1;
pos = strchr (lastpos, G_DIR_SEPARATOR);
}
if (dest != lastpos) strcpy (dest, lastpos);
return g_strreverse (abspath);
}
/*
The bug here is that glib2 only looks at POSIX-styled path separators: /
while the Vita can interprate both Windows-styled \ and POSIX-styled /
It's not much, but it's something to start with.
In order to disable the security manager, first you must make mono think
that the library being loaded is a system library. Sony has hard coded
the names "mscorlib.dll", "System.dll", and "Sce.PlayStation.Core.dll"
and does a strcmp() with the canonical path of the library being loaded.
However, if Mono sees that the basename of the library being loaded is
"mscorlib.dll" or any of the other two, it will try to load the embedded
version instead of our own.
So, what we need to do is feed a path to Assembly.LoadFile() that a) the
Vita recognizes. b) gets past the security manager by thinking it's a core
library, and c) gets past mono's loader by thinking it's an external library.
This might be impossible and it might be not. Included are the following functions:
gg_path_get_basename: This is the actual function on the Vita to get the basename
used by eglib on the Vita. Renamed to not conflict with the host's glib2.
Maybe there a bug in getting the basename?
mono_path_canonicalize: This is the actual function on the Vita to canonicalize
the path by mono. Maybe a bug in creating the name?
make_vita_path: My own function to simulate what the Vita IO manager sees. Don't
worry about this one much and it may have bugs so don't rely on it.
You can play around with different path inputs either by passing it as the first
command line argument or by modifying the hard coded input. You need glib2 to compile.
Try not to compile on Windows since we don't want to screw with the separators.
Some ideas:
play with .. and .
using \\/ in a path
using weird ASCII characters (\0\b anyone?)
*/
int main(int argc, const char * argv[])
{
const char *input;
if(argc > 1)
input = argv[1];
else
input = "pss0:\\top\\Applications\\a/../\\mscorlib.dll";
char *vita = make_vita_path(input);
char *basename = gg_path_get_basename(input);
char *cano = mono_path_canonicalize(input);
fprintf(stderr, "Inpt: %s\nVita: %s\nBase: %s\nCano: %s\n\n", input, vita, basename, cano);
fprintf(stderr, "Check 1) Base CANNOT be \"%s\": %s\n", "mscorlib.dll", strcmp(basename, "mscorlib.dll") == 0 ? "FAILED" : "PASSED");
fprintf(stderr, "Check 2) Canonical path MUST be \"%s\": %s\n", "mscorlib.dll", strcmp(cano, "mscorlib.dll") == 0 ? "PASSED" : "FAILED");
fprintf(stderr, "Check 3) Vita path MUST be \"%s\": %s\n", "pss0:/top/Applications/mscorlib.dll", strcmp(vita, "pss0:/top/Applications/mscorlib.dll") == 0 ? "PASSED" : "FAILED");
fprintf(stderr, "If all three checks passed, you can hack the Vita!");
free(vita);
free(basename);
free(cano);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment