Tuesday, March 24, 2009

sharpen your .NET skills



I have had dozens of discussions about C#; being a secure
language and that CLR/VM based languages should be used
with new projects in order to increase security. One argument
is that memory corruption can't happen any longer.
I agree, but always point out that C# code is not secure
automagically, even if the programmers code is correct.
The runtime might be buggy as well! I recently read an
article in the famous german iX magazine about security measurements
in .NET. One of the measures is the so called IsolatedStorage
which allows you to store data in a secure way. Much like
a database, based on a token you can store/retrieve data
without your real filesystem being at risk. Nice thing,
and I coded an example-server:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.IO;
using System.IO.IsolatedStorage;

class Server {

private static void store(string key, Byte[] b)
{
try {
Console.WriteLine("Isolated storage @ {0}", key);
IsolatedStorageFileStream fs = new IsolatedStorageFileStream(key, FileMode.Create);
fs.Write(b, 0, b.Length);
fs.Close();
} catch {
Console.WriteLine("Exception!");
}
}

private static Byte[] load(string key)
{
Byte[] b = new Byte[256];
try {
Console.WriteLine("IsolatedStorage load @ {0}", key);
IsolatedStorageFileStream fs = new IsolatedStorageFileStream(key, FileMode.Open);
fs.Read(b, 0, b.Length);
fs.Close();
} catch {
Console.WriteLine("Exception!");
}
return b;
}
public static void Main()
{
Byte[] buf = new Byte[256];
int cnt;

string data = "";

ASCIIEncoding ascii = new ASCIIEncoding();
TcpListener l = new TcpListener(8080);
l.Start();
try {
Socket s = l.AcceptSocket();
while (data.Trim() != "quit") {
Array.Clear(buf, 0, buf.Length);
if ((cnt = s.Receive(buf, buf.Length, 0)) == 0)
break;
data = ascii.GetString(buf, 0, cnt);
Console.WriteLine("Received: {0}", data.Trim());
if (data.StartsWith("store ")) {
Array.Clear(buf, 0, buf.Length);
if (s.Receive(buf, buf.Length, 0) == 0)
break;
store(data.Substring(6, data.Length - 6).Trim(), buf);
} else if (data.StartsWith("load ")) {
Byte[] result = load(data.Substring(5, data.Length - 5).Trim());
s.Send(result);
}
}
} catch {
Console.WriteLine("Exception!");
}
l.Stop();
}
}

You can connect to the server on TCP port 8080 and
store/load data via the telnet interface for example.
Beside the easy of code and the fact that it treats
TCP streams like messages which could make trouble in
real networking environments, this code should be correct.
It fits perfectly as a localhost example. There is just
a problem with the IsolatedStorage itself!
Some versions of the mono runtime do not remove
"../" character sequences from the path component as it
should. So, depending on your configuration you can
obtain funny results. On a openSUSE 11.1, the storage
place is in ~/.config/.isolated-storage/[some-hash]/.
An attacking scenario is inside the xterm.
I already informed the maintainers and a fix is underway.
Its not a big issue, and I dont have any application in mind
that is actually vulnerable and uses IsolatedStorage this way.

File-system/storage tricks will be a major playground for
.NET/C# applications in future. In a non-public review
of a larger C# based "system" it turned out that it was possible to obtain
local root privileges by loading evil assemblies as
a result of tricking the application.
Additionally, the managed runtime may provide
(depending on the implementation) all the
nasty things that we got rid of in native CPUs during
the last years: executable data, fixed addresses etc.



No comments: