Alexander Huber bio photo

Alexander Huber

Software guy, full-stack web developer, full-time computer geek. Blogs in English and German.

Twitter Github

Tags

Wie gültige Session-IDs für das Webinterface einer FRITZ!Box erzeugt werden ist eigentlich ziemlich gut in einem der vielen Docs beschrieben. Sogar mit Codebeispielen in C#, wow! Will man die Codebeispiele jedoch auf eine andere Programmiersprache wie Python oder Ruby portieren gibt es eine Überraschung. Der Code

public string GetResponse (string challenge, string kennwort) {
    return GetMD5Hash(challenge + "-" + kennwort);
}

sieht doch eigentlich ziemlich beherrschbar aus. Dasselbe Ergebnis sollte sich doch bestimmt mit

def get_response(challenge, kennwort)
  Digest::MD5.hexdigest(challenge + "-" + kennwort)
end

generieren lassen? Leider nein. Es werden für dieselben Inputs unterschiedliche Hashes erzeugt. Der Teufel steckt wieder mal im Detail, bzw. in der GetMD5Hash-Methode:

1
2
3
4
5
6
7
8
9
public string GetMD5Hash (string input) {
    MD5 md5Hasher    = MD5.Create();
    byte[] data      = md5Hasher.ComputeHash(Encoding.Unicode.GetBytes(input));
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.Length; i++) {
        sb.Append(data[ i ].ToString("x2"));
    }
    return sb.ToString();
}

Hier sieht auf den ersten Blick alles super aus. x2 bedeutet, dass der String als Hex-Wert angehängt wird, das Ergebnis sollte also auf jeden Fall hexdigest entsprechen. Wirft man jedoch den Debugger an, sieht man dass mit GetBytes() ein Array erzeugt wird, in dem jedes zweite Element eine 0 ist. Glücklicherweise hat Ruby eine elegante Vorgehensweise, schnell jedes zweite Element eines Arrays zu setzen. Eine neue get_response-Methode macht also noch ein wenig mehr:

1
2
3
4
5
6
7
8
def get_response(challenge, kennwort)
  before  = challenge + "-" + kennwort
  ary     = []
  before.size.times { ary << 0 }
  after   = before.bytes.zip(ary).flatten!
  unicode = after.pack('U*')
  Digest::MD5.hexdigest(unicode)
end

Das Verwenden von Unicode wird gleich mit der Umwandlung eines Byte-Arrays mit pack() erschlagen. Nun sind die erzeugten Hashes sowohl mit Ruby- als auch mit C#-Programm endlich identisch.


Twitter

Blogroll