Last active
August 30, 2018 19:31
-
-
Save Logioniz/1111c5c3167de70c07933a67e69e20cc to your computer and use it in GitHub Desktop.
rucaptcha аналог (решение гугл рекапчи с помощью работников/людей)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 компоненты: | |
1. простенький api service, который чем-то похож на апи rucaptcha для приёма recaptcha. Нужно передать рекапча ключ и урл. | |
2. простенький socks5 прокси, который умеент подменять запросы, когда это нужно (чтобы домен (url) совпадал с доменом разгадываемой капчей) | |
3. браузер работника должен быть настроен на проксирование через наш socks5 прокси | |
Первым делом нужно запустить прокси и апи серверы. | |
Потом нужно дождаться, когда подключится работник (тот, кто капчу разгадывает), при этом работник должен настроить в браузере прокси socks5 с разыменование домена. | |
Потом сделать запрос на добавление капчи: | |
curl -XPOST --data-urlencode "googlekey=<публичный ключ рекапчи>" --data-urlencode "pageurl=<страница на которой разгадываем>" "http://127.0.0.1:3000/in.php" | |
Далее работник должен разгадать капчу, после чего ответ уйдёт апи сервису. | |
И клиент может забирать результат запросом: | |
curl "http://127.0.0.1:3000/res.php?id=<id, выданные на этапе добавления капчи>" | |
Так же можно проверить результат, если у вас есть секретный ключ от рекапчи: | |
curl --data-urlencode "secret=<секретный ключ>" --data-urlencode "response=<ответ для рекапчи>" --data-urlencode "remoteip=<ip адрес, не обязательный параметр>" "https://www.google.com/recaptcha/api/siteverify" | |
На момент написания, гугл не проверял ip адрес разгадывающего. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
use Mojo::Base -strict; | |
use Mojolicious::Lite; | |
use Mojo::URL; | |
use DDP; | |
my $server = '127.0.0.1:3000'; | |
my ($tasks, $workers) = ({}, {}); | |
# worker | |
get '/' => {host => $server} => 'index'; | |
websocket '/ws' => sub { | |
my $c = shift; | |
$c->inactivity_timeout(30000); | |
$workers->{$c} = $c; | |
$c->on(message => sub { }); | |
$c->on(finish => sub { | |
my ($c, $code, $reason) = @_; | |
delete $workers->{$c} if exists $workers->{$c}; | |
}); | |
}; | |
get '/result' => sub { | |
my $c = shift; | |
my $url = $c->param('url'); | |
my $resp = $c->param('g-recaptcha-response'); | |
say $resp; | |
for my $id (keys %$tasks) { | |
my ($u, $key, $handle) = @{$tasks->{$id}}{qw/url key handle/}; | |
$tasks->{$id}{resp} = $resp if $url eq $u; | |
} | |
return $c->redirect_to("//$server/"); | |
}; | |
# proxy | |
get '/domain/check' => sub { | |
my $c = shift; | |
my $domain = $c->param('domain'); | |
my ($res_key, $res_url, $is_found) = 0; | |
for my $id (keys %$tasks) { | |
my ($url, $key, $handle) = @{$tasks->{$id}}{qw/url key handle/}; | |
if ($url =~ m/$domain/ and !$handle) { | |
$is_found = 1; | |
$tasks->{$id}{handle} = 1; | |
($res_key, $res_url) = ($key, $url); | |
} | |
} | |
return $c->render(json => {status => 'fail'}) unless $is_found; | |
$c->render(json => { | |
status => 'ok', | |
key => $res_key, | |
url => $res_url | |
}); | |
}; | |
# client | |
any 'in.php' => sub { | |
my $c = shift; | |
my $key = $c->param('googlekey'); | |
my $url = $c->param('pageurl'); | |
my $id = 100000000 + int rand 100000000; | |
unless (keys %$workers) { | |
return $c->render(code => 500, text => 'workers not found'); | |
} | |
$url =~ s/https:\/\//http:\/\//; | |
$tasks->{$id} = {url => $url, key => $key, handle => 0}; | |
my $worker = delete $workers->{(keys %$workers)[0]}; | |
$worker->send($url); | |
$c->render(text => "OK|$id"); | |
}; | |
get 'res.php' => sub { | |
my $c = shift; | |
my $id = $c->param('id'); | |
return $c->reply->not_found unless $tasks->{$id}; | |
if ($tasks->{$id}{resp}) { | |
my $task = delete $tasks->{$id}; | |
return $c->render(text => "OK|" . $task->{resp}); | |
} | |
return $c->render(text => 'CAPCHA_NOT_READY'); | |
}; | |
app->start; | |
__DATA__ | |
@@ index.html.ep | |
Waiting google recaptcha... | |
<script> | |
var ws = new WebSocket("ws://<%= $host %>/ws"); | |
ws.onmessage = function(event) { | |
var url = event.data; | |
alert("Goto " + url); | |
window.location.href = url; | |
}; | |
ws.onerror = function(error) { | |
alert("Error " + error.message); | |
}; | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
use Mojo::Base -strict; | |
use Mojo::IOLoop; | |
use Devel::Peek; | |
use Data::Dumper; | |
use Mojo::UserAgent; | |
use Mojo::URL; | |
my $api_url = "http://127.0.0.1:3000"; | |
my $domain_check_url = "$api_url/domain/check"; | |
my $result_url = "$api_url/result"; | |
my $DEBUG = 0; | |
my $users = { | |
# user1 => 'pass1' | |
}; | |
sub _debug { | |
my ($type, @params) = @_; | |
say '-----------'; | |
say $type; | |
my (@columns, @values); | |
while (@params) { | |
my ($c, $v) = splice @params, 0, 2; | |
push @columns, sprintf('%15s|', $c); | |
push @values, sprintf('%15s|', $v); | |
} | |
say join '', @columns if @columns; | |
say join '', @values if @values; | |
say ''; | |
} | |
sub init_handshake { | |
my ($stream, $bytes) = @_; | |
my ($ver, $nmethods, @methods) = unpack 'C*', $bytes; | |
_debug('Receive', ver => $ver, nmethods => $nmethods, methods => join('', @methods)) if $DEBUG; | |
if ($ver != 5) { | |
_debug('Close connection') if $DEBUG; | |
return $stream->close; | |
} | |
my %methods = map { $_ => 1 } @methods; | |
if (%$users and ! $methods{2}) { | |
$stream->write(pack'C*', 5, 0xff); | |
if ($DEBUG) { | |
_debug('Send', ver => 5, method => 0xff); | |
_debug('Close connection'); | |
} | |
return $stream->close; | |
} | |
if (! %$users and ! $methods{0}) { | |
$stream->write(pack'C*', 5, 0xff); | |
if ($DEBUG) { | |
_debug('Send', ver => 5, method => 0xff); | |
_debug('Close connection'); | |
} | |
return $stream->close; | |
} | |
$stream->unsubscribe(read => $stream->subscribers('read')->[-1]); | |
%$users | |
? $stream->on(read => \&auth_handshake) | |
: $stream->on(read => \&command_handshake); | |
$stream->write(pack'C*', 5, %$users ? 2 : 0); | |
_debug('Send', ver => 5, method => %$users ? 2 : 0) if $DEBUG; | |
} | |
sub auth_handshake { | |
my ($stream, $bytes) = @_; | |
my ($ver, $ulen) = unpack 'C*', substr($bytes, 0, 2); | |
$bytes = substr($bytes, 2); | |
my $uname = substr($bytes, 0, $ulen); | |
$bytes = substr($bytes, $ulen); | |
my $plen = unpack 'C', substr($bytes, 0, 1); | |
$bytes = substr($bytes, 1); | |
my $passwd = substr($bytes, 0, $plen); | |
my $status = 1; | |
$status = 0 if $users->{$uname} && $users->{$uname} eq $passwd; | |
_debug('Receive', ver => $ver, ulen => $ulen, uname => $uname, plen => $plen, passwd => $passwd) if $DEBUG; | |
if ($ver != 1 || $status != 0) { | |
$stream->write(pack'C*', 1, 1); | |
if ($DEBUG) { | |
_debug('Send', ver => 1, status => 1); | |
_debug('Close connection'); | |
} | |
return $stream->close; | |
} | |
$stream->unsubscribe(read => $stream->subscribers('read')->[-1]); | |
$stream->on(read => \&command_handshake); | |
$stream->write(pack'C*', 1, $status); | |
_debug('Send', ver => 1, status => $status) if $DEBUG; | |
} | |
sub command_handshake { | |
my ($stream, $bytes) = @_; | |
my ($ver, $cmd, $rsv, $atyp) = unpack 'C*', substr($bytes, 0, 4); | |
$bytes = substr($bytes, 4); | |
my $addr; | |
if ($atyp == 1) { | |
$addr = join '.', unpack('C4', $bytes); | |
$bytes = substr($bytes, 4); | |
} elsif ($atyp == 3) { | |
my $addr_len = unpack 'C', substr($bytes, 0, 1); | |
$bytes = substr($bytes, 1); | |
$addr = substr($bytes, 0, $addr_len); | |
$bytes = substr($bytes, $addr_len); | |
} elsif ($atyp == 4) { | |
$addr = join ':', unpack('(H4)8', $bytes); | |
$bytes = substr($bytes, 16); | |
} | |
my $port = unpack 'n', $bytes; | |
_debug('Receive', ver => $ver, cmd => $cmd, rsv => $rsv, atyp => $atyp, addr => $addr, port => $port) if $DEBUG; | |
my $rep = 0; | |
$rep = 1 if $ver != 5; | |
$rep = 7 if $cmd != 1; | |
$rep = 8 if $atyp != 1 && $atyp != 3 && $atyp != 4; | |
if ($rep != 0) { | |
$stream->write(pack'C*', 5, $rep, 0, 1, 0, 0, 0, 0, 0, 0); | |
if ($DEBUG) { | |
_debug('Send', ver => 5, rep => $rep, rsv => 0, atyp => 1, addr => '0.0.0.0', port => 0); | |
_debug('Close connection'); | |
} | |
return $stream->close; | |
} | |
my $ua = Mojo::UserAgent->new; | |
my $res = $ua->get(Mojo::URL->new($domain_check_url)->query(domain => $addr))->result->json; | |
if ($res->{status} eq 'ok') { | |
use DDP; | |
p $res; | |
$stream->unsubscribe(read => $stream->subscribers('read')->[-1]); | |
$stream->on(read => sub { spoofing(@_, $res) }); | |
$stream->write(pack('C*', 5, 0, 0, 1) . pack('C*', 127, 0, 0, 1) . pack('n', 31337)); | |
if ($DEBUG) { | |
_debug('Send', ver => 5, rep => 0, rsv => 0, atyp => 1, addr => '127.0.0.1', port => '31337'); | |
} | |
return; | |
} | |
Mojo::IOLoop->client({address => $addr, port => $port, timeout => 5} => sub { | |
my ($loop, $err, $foriegn_stream) = @_; | |
if ($err) { | |
my $rep = 0xff; | |
$rep = 3 if $err =~ m/Transport endpoint is not connected/; | |
$rep = 4 if $err =~ m/Can't resolve/; | |
$rep = 4 if $err =~ m/Can't connect/; | |
$rep = 5 if $err =~ m/Connection refused/; | |
$rep = 6 if $err =~ m/timeout/; | |
$stream->write(pack'C*', 5, $rep, 0, 1, 0, 0, 0, 0, 0, 0); | |
if ($DEBUG) { | |
say $err; | |
_debug('Send', ver => 5, rep => $rep, rsv => 0, atyp => 1, addr => '0.0.0.0', port => 0); | |
_debug('Close connection'); | |
} | |
return $stream->close; | |
} | |
if (!$stream->handle or !$stream->handle->connected) { | |
$stream->close; | |
return $foriegn_stream->close; | |
} | |
$stream->timeout(0); | |
$foriegn_stream->timeout(0); | |
$stream->unsubscribe(read => $stream->subscribers('read')->[-1]); | |
my $state = {}; | |
io_streams($state, 1, $stream, $foriegn_stream); | |
io_streams($state, 0, $foriegn_stream, $stream); | |
my $localaddr = $foriegn_stream->handle->sockaddr; | |
my $localport = $foriegn_stream->handle->sockport; | |
my $atyp = length $localaddr > 4 ? 4 : 1; | |
$stream->write(pack('C*', 5, 0, 0, $atyp) . $localaddr . pack('n', $localport)); | |
if ($DEBUG) { | |
my ($template, $separator) = $atyp == 1 ? ('C*', '.') : ('(H4)*', ':'); | |
my $localaddr_str = join $separator, unpack $template, $localaddr; | |
_debug('Send', ver => 5, rep => 0, rsv => 0, atyp => $atyp, addr => $localaddr_str, port => $localport); | |
} | |
}); | |
} | |
sub io_streams { | |
my ($state, $is_client, $stream1, $stream2) = @_; | |
$stream1->on('close' => sub { | |
$stream2->close; | |
undef $stream2; | |
}); | |
$stream1->on('error' => sub { | |
my ($stream, $err) = @_; | |
$stream2->close; | |
undef $stream2; | |
}); | |
$stream1->on('read' => sub { | |
my ($stream, $bytes) = @_; | |
$stream2->write($bytes); | |
}); | |
} | |
sub spoofing { | |
my ($stream, $bytes, $obj) = @_; | |
my ($key, $url) = @$obj{qw/key url/}; | |
my $message = " | |
<script src='https://www.google.com/recaptcha/api.js'></script> | |
<form action='$result_url' method='GET'> | |
<div class='g-recaptcha' data-sitekey='$key'></div> | |
<input type='text' name='url' value='$url'><br> | |
<input type='submit'> | |
</form> | |
"; | |
my $host = Mojo::URL->new($url)->host; | |
my $content_length = length $message; | |
$stream->write("HTTP/1.1 200 OK\r | |
Host: $host\r | |
Content-Type: text/html\r | |
Connection: close\r | |
Content-Length: $content_length\r | |
\r | |
$message | |
" => sub { | |
my $stream = shift; | |
$stream->close; | |
}); | |
} | |
Mojo::IOLoop->server({port => 3001} => sub { | |
my ($loop, $stream) = @_; | |
$stream->timeout(10); | |
$stream->on(read => \&init_handshake); | |
}); | |
Mojo::IOLoop->start unless Mojo::IOLoop->is_running; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment