web-dev-qa-db-ja.com

使用されているハッシュアルゴリズムを見つけるにはどうすればよいですか?

クライアントの1人から、理事会メンバーが資料にアクセスするために使用するログインページを変更するように依頼されました。ここでの問題は、以前これを担当した人がドキュメントを残しておらず、パスワードが何らかの(一見単純な)アルゴリズムで暗号化されていることです。

私はハッシュにアクセスでき、パスワードが何であるかを知っています。 JavaScriptはハッシュをパスワードとして使用しています。私の考えは、アルゴリズムが何であるかを理解できれば、彼らの要求に合うように新しいアカウントを作成できるということです。

アルゴリズムを確認する方法はありますか?

ユーザーはドロップダウンメニューから名前を選択するように求められ、パスワードはHTMLコードの名前の横にあるハッシュに関連付けられます。フォームオプションは次のようになります(Nは数値を示し、Lは文字を示します)。

<option value='Username|NNNNN|LLLLLLLL'>Username

値を解析する実際のスクリプトは次のようになります

<SCRIPT LANGUAGE="JavaScript">
<!-- Begin
var params = new Array(4);
var alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHI";

function check(form) {
    which = form.memlist.selectedIndex;
    choice = form.memlist.options[which].value + "|";
    if (choice == "x|") {
        alert("Please Select Your Name From The List");
        return;
    }
    p = 0;
    for (i = 0; i < 3; i++) {
        a = choice.indexOf("|", p);
        params[i] = choice.substring(a, p);
        p = a + 1;
    }
    h1 = makehash(form.pass.value, 3);
    h2 = makehash(form.pass.value, 10) + " ";
    if (h1 != params[1]) {
        alert("Incorrect Password!");
        return;
    };
    var page = "";
    for (var i = 0; i < 8; i++) {
        letter = params[2].substring(i, i + 1)
        ul = letter.toUpperCase();
        a = alpha.indexOf(ul, 0);
        a -= (h2.substring(i, i + 1) * 1);
        if (a < 0) a += 26;
        page += alpha.substring(a, a + 1);
    };
    top.location = page.toLowerCase() + ".html";
}

function makehash(pw, mult) {
    pass = pw.toUpperCase();
    hash = 0;
    for (i = 0; i < 8; i++) {
        letter = pass.substring(i, i + 1);
        c = alpha.indexOf(letter, 0) + 1;
        hash = hash * mult + c;
    }
    return (hash);
}
// End -->
</script>

とにかくこれをリバースエンジニアリングして、新しいユーザーアカウントを作成することはできますか?

10
DKNUCKLES

ここでのアルゴリズムは次のとおりです。

function makehash(pw, mult) { // Password and... multiplier?
    pass = pw.toUpperCase(); // Case insensitivity
    var hash = 0;
    for (i = 0; i < Math.min(pass.length, 8); i++) { // 8 char passwords max...
        c = pass.charCodeAt(i) - 63; // A = 2, B = 3, etc.
        hash *= mult;
        hash += c;
    }

    return hash;
}

コードを少し整理してコメントを追加しました。これを書いた人は、コーディング、セキュリティ、数学の分野ではまったく能力がありません。とにかく、それはMD5やAESのような「公式の」アルゴリズムではなく、自作であり、信じられないほどフォールトトレラントではありません。文字のみを受け入れ、大文字と小文字を区別せず、最初の8文字以降のすべての文字を無視します。

全員のパスワードハッシュをアップグレードすることを強くお勧めします。参照: パスワードを安全にハッシュする方法?

ちなみに、これはいくつかの書式設定をした残りのコードです。

var params=new Array(4);
var alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHI";

function check(form) {
    which = form.memlist.selectedIndex;
    choice = form.memlist.options[which].value + "|";
    if (choice == "x|") {
        alert("Please Select Your Name From The List");
        return;
    }

    p = 0;
    for (i = 0; i < 3; i++) {
        a = choice.indexOf("|", p);
        params[i] = choice.substring(a, p);
        p = a + 1;
    }
    h1 = makehash(form.pass.value, 3);
    h2 = makehash(form.pass.value, 10) + " ";
    if (h1 != params[1]) {
        alert("Incorrect Password!");
        return;
    }

    var page = "";
    for (var i = 0; i < 8; i++) {
        letter = params[2].substring(i, i + 1)
        ul = letter.toUpperCase();
        a = alpha.indexOf(ul, 0);
        a -= h2.substring(i, i + 1) * 1; // multiplying by one? Seriously?
        if (a<0)
            a+=26;
        page += alpha.substring(a, a + 1);
    };
    top.location=page.toLowerCase() + ".html";
}

私はコメントを追加しますが、この混乱の中で何らかの理由を見つけることが価値があるかどうかはわかりません。

25
Luc

コードのセクションを検索すると、このページが表示されます。

http://www.yaldex.com/FSPassProtect/LoginCoder.htm

他の多くの場所の間で。これにより、別のエントリを追加する方法がわかります。

ただし、これは極端に安全でない認証メカニズムであり、ページを推測するだけで、リモートの有能な人物によって簡単にバイパスされる可能性があります(大文字と小文字は区別されません)。 .htmlで終わる8文字の文字列)。パスワードを正しく入力するとリダイレクトされます。実際、26 ^ 8の異なるURLを試す簡単なスクリプトは必要ありません。あなたがしなければならないすべては、エンコードされた値「BAFJFFCI」を見て、 10 ^ 8 ユーザーが1人だけの場合は9 ^ 7。複数のユーザーが同じページにアクセスする場合、スペースを大幅に削減できます。


わかりました、これがどのように機能するかを分解しました。ログインフォームのすべてのユーザーには、「ユーザー名| 35240 | BAFJFFCI」のような値を持つオプションがあります。ユーザー名はどこにも使用されておらず、無関係です。入力されたパスワード文字列に対して、乗数3でmake_hashを呼び出すことにより、h1と呼ばれる次のハッシュが生成されます。

_def make_hash(pw, mult):
    hash = 0
    for char in pw:
        char_ind = ord(char) - ord('A') +1 # A=1, B=2, C=3, ...
        hash *= mult
        hash += char_ind
    return hash
_

したがって、パスワードが「安全でない」場合、それは数字に変換されます(I = 9、n = 14、s = 19、...)[9,14,19,5,3,21,18,5]ハッシュは3*(3*(3*(3*(3*(3*(3*(9)+14)+19)+5)+3)+21)+18)+5で、35240になります。大文字と小文字は区別されません。では、2番目の値は何ですか?有効なパスワードを入力すると、秘密のページに転送されます。私の場合、秘密のページは「AAAAAAAA」で、フィールド「BAFJFFCI」からデコードされます

デコードすると、乗数10の別のハッシュが生成されます。h2=make_hash(pw, 10)これは、私の場合、h2 = 105955285と計算されます。左から始まる各桁は、シフトする文字数を教えてくれます。たとえば、「AAAAAAAA」の場合、最初の文字を1文字前方にシフトすると「B」、2番目の文字を前方に0シフトすると「A」、「A」を5文字前方にシフト、「F」は9文字前方に移動します。 「パスワードから生成された」h2を使用して「BAFJFFCI」から「AAAAAAAA」を復元するのと同様に、単に文字を減算します。

攻撃者としてこれは、パスワードフィールドに各ユーザーのハッシュが表示されることを意味し、ソースページには10文字しかありません。したがって、10 ^ 8のWebページを試して確認することができます。複数のユーザーがすべて同じページに転送された場合、宛先ページを簡単に見つけて、認証手順全体をバイパスしてそこに行くことができます。

たとえば、「GCDDGDAJ」と「BAFJFFCI」はどちらもaaaaaaaa.htmlを指すようにシフトバージョンにシフトされます。両方が同じページに移動すると想定し、「G」、「B」の両方が同じ文字から0〜9文字シフトして発生することを確認すると、最初の文字はA、B、X、Y、Zのいずれかです。私は、2つのh2が同じページに転送する可能性があるかどうかを簡単に確認できます。たとえば、9文字以上離れていないことを確認します(たとえば、1つのパスワードに「A」があり、もう1つのパスワードに同じ場所がある場合) K、L、M、N、O、P、またはQの場合、パスワードは同じページに移動できません)。また、距離が離れていると、実際のスポットの値も制限されます。


そしてより多くの暗号分析のために:

また、このハッシュの衝突を非常に簡単に見つけることができます。簡単にするために、このスキームで生成された3文字のパスワードを分析します。したがって、h1 = 3 *(3 * c1 + c2)+ c3)になります。 3に関して係数(シンボル_%_で除算した後の剰余)を取ると、h1%3 = c3%3であることがわかります。したがって、h1%3 = 0の場合、最後の文字はCFILORUXにあります。 h1%3 = 1の場合、ADGJMPSVYにあります。 h1%3 = 2の場合、BEHKNQTWZにあります。この手順を使用して、3文字のパスワードを最大9 ^ 2 = 81の可能性に減らすことができます。

たとえば、pwが「THE」で、ハッシュが3 *(3 * 20 + 8)+ 5 = 209だったとします。 209%3 = 2として;最後の文字はBEHKNQTWZにあることがわかります。最後の文字から9つの選択肢をすべて差し引いて、3で割り、もう一度繰り返します。たとえば、最後の文字がBの場合、(209-2)/ 3 = 69は69%3 = 0を意味し、中央の文字はCFILORUXにあります。 Cの場合、最初の文字は(69-3)= 22 = Vでなければなりません。したがって、VCBのハッシュは209です。これを他の選択肢(UFB、TIB、SLB、ROB、QRB、PUB、OXB)について繰り返します。最後の文字がEだった場合、 VBE、UEE、THE、SKE、RNE、QQE、PTE、OWE、NZEがあります。これは簡単に繰り返すことができます。したがって、最大9 ^ 2 = 81の選択肢があります(残りの文字で決まる最後の文字以外の各文字。8または9の選択肢があります)。私たちの場合、78の選択肢があります。

VCB UFB TIB SLB ROB QRB PUB OXB VBE UEE THE SKE RNE QQE PTE OWE NZE VAH UDH TGH SJH RMH QPH PSH OVH NYH UCK TFK SIK RLK QOK PRK OUK NXK UBN TEN SHN RKN QNN PQN OQ NPNQQ NQQ NVQ MYQ TCT SFT RIT QLT POT ORT NUT MXT TBW SEW RHW QKW PNW OQW NTW MWW LZW TAZ SDZ RGZ QJZ PMZ OPZ NSZ MVZ LYZ

これらには、URLを取得するために必要な78個のmult = 10ハッシュがあります(ソートしました)。

1476 1483 1546 1553 1560 1567 1574 1616 1623 1630 1637 1644 1651 1658 1665 1686 1693 1700 1707 1714 1721 1728 1735 1742 1756 1763 1770 1777 1784 1791 1798 1805 1812 1826 1833 1840 1847 1854 1861 1868 1875 1882 1896 1903 1910 1917 1945 19945 1931 1931 1931 1931 1931 1945 1952 1966 1973 1980 1987 1994 2001 2008 2015 2022 2036 2043 2050 2057 2064 2071 2078 2085 2092 2127 2134 2141 2148 2155 2162 2218 2225 2232

URLの変更では、mult-10ハッシュの最初の3桁のみが重要であるため、実際には62の異なる値しかなかったことに注意してください。または、単純にするために、最大/最小のmodハッシュを見つけて、その間のすべてについて最初の3桁を確認することもできます(77の値を確認します。VCBとLYZを見つけた後)。

完全なmult-10ハッシュはすべて同じ値mod 7(すべて6)を持っていることに注意してください。このパターン(同じ余りを10-3 = 7で割ったもの)は一般的に当てはまります。したがって、最初と最後の許容パスワード「VCB」がmult = 10ハッシュ2232を与え、「LYZ」が1476のmult-10ハッシュを与えてすべてを試すこともできます((2232-1476)/ 7)+ 1 = 109 7で区切られた範囲内のmult-10ハッシュは、mult-10ハッシュの最初のn桁のみを使用します。

繰り返しになりますが、方法に関係なく、数百万のURLをチェックする必要があります。これは100 /秒の速度で1日以内に完了するはずです。

16
dr jimbob

このハッシュアルゴリズムは非常に安全でないため、考えられるすべてのパスワードとページの組み合わせを計算するのに約2分かかります。サーバーで実際に確認する必要のある20000〜2500000の可能な秘密ページのリストを生成します。たとえば、「INSECURE」から「aaaaaaaa.html」への@drjimbobの例では、26063の可能な秘密ページのみをチェックする必要があります。他の人は順調でしたが、実際にはそれほどではありませんでした。実際、このパスワードスキームは、ユーザーにhtmlページへのURLを要求するだけの場合と比較して、実際にはセキュリティを弱めます。その場合、攻撃者は26 ^ 8の可能なページをチェックする必要がありましたが、パスワードスキームにより、攻撃者はこれらすべての26 ^ 8の可能性の0.00122%のみをチェックする必要があります。

>>> # "insecure", "aaaaaaaa.html"
>>> len(set(y for x,y in find_pages(35240, 'BAFJFFCI')))
3248895

>>> # "password", "mainpage.html"
>>> len(set(y for x,y in find_pages(42691, 'NGLOQEMM')))
2988569

>>> # "theirpwd", "endpages.html"
>>> len(set(y for x,y in find_pages(52219, 'GNLVAPMV')))
3035974

>>> # "asdfvcxz", "nowheres.html"
>>> len(set(y for x,y in find_pages(18215, 'PXAPGWKY')))
2382856

>>> # "zaqxswde", "logintop.html"
>>> len(set(y for x,y in find_pages(64403, 'NUIRTURT')))
2792596




import string
chars = [(c, ord(c) - ord('A') + 1) for c in string.uppercase]
chars = chars[2::3], chars[::3], chars[1::3]
chars = [list(reversed([(a,b,c) for a,(b,c) in enumerate(chs)])) for chs in chars]

def reverse_make_hash(hash, num=8, cur=''):
    """ generates a list of pw such that `make_hash(pw, 3) == hash` """
    if num <= 0 or hash <= 0: return
    if num == 1 and hash > 26: return
    if num == 1 and hash <= 26: 
        yield chr(ord('A') + hash - 1) + cur

    mod = hash % 3
    for i,c,v in chars[mod]:
        n = (hash - v) / 3
        for pot in reverse_make_hash(n, num-1, c + cur): yield pot

# from dr jimbob
def make_hash(pw, mult):
    hash = 0
    for char in pw:
        char_ind = ord(char) - ord('A') +1 # A=1, B=2, C=3, ...
        hash *= mult
        hash += char_ind
    return hash

def find_pages(hash, page):
    results = []
    for p in reverse_make_hash(hash): 
        page_url = ''.join(chr((ord(p)-int(h)-ord('A')) % 26 +ord('A')) for p,h in Zip(page, str(make_hash(p, 10))))
        if page_url.isalpha():
            print p, page_url
            results.append((p, page_url.lower() + '.html'))
    return results

a = find_pages(35240, 'BAFJFFCI')  # "insecure", "aaaaaaaa.html"
b = find_pages(42691, 'NGLOQEMM')  # "password", "mainpage.html"
c = find_pages(52219, 'GNLVAPMV')  # "theirpwd", "endpages.html"
d = find_pages(18215, 'PXAPGWKY')  # "asdfvcxz", "nowheres.html"
e = find_pages(64403, 'NUIRTURT')  # "zaqxswde", "logintop.html"
3
Lie Ryan