sudo --stdin 및 임의의 문자가 포함된 비밀번호를 사용하는 zsh의 이상한 동작

sudo --stdin 및 임의의 문자가 포함된 비밀번호를 사용하는 zsh의 이상한 동작

이 게시물은 여러분이 예상하거나 매일 볼 수 있는 것처럼 시작되지 않습니다. 여기 제 루트 비밀번호가 있습니다. :4\g&8n:6_F[`9Touc8Ls+L'8)>6!3,nNmUzR&Ub~w7NTd'^Lb0]`0`."u>tP\>XAspMLTt!@}=F6CP)NsSMYY7*xm'A!7`!n'tmAaGWoBhS|u4{k$*v/o|'%)mbXMw
기억하기 어렵죠? 글쎄요, 그건 문제가 되지 않습니다. 네가 나한테 소리 지르기 전에, 응, 내가 바꿨어. 이제 dummy_root테스트를 위해 해당 비밀번호를 가진 사용자가 있습니다 .

긴 이야기 짧게

이 비밀번호를 stdin의 sudo에 전달하기 위해 zsh를 깨뜨렸습니다.

(매우) 긴 버전

따라서 이것은 잘못된 것입니다. 저는 sudowith targetpw플래그를 사용하고 있습니다. 이는 sudo가 요구하는 비밀번호가 내 비밀번호가 아닌 대상 사용자의 비밀번호임을 의미합니다. 저는 pass비밀번호 관리자(장소), 내 모든 컴퓨터/서버의 루트 비밀번호를 포함합니다. 확실한 보안상의 이유로 비밀번호는 128자 패스에서 무작위로 생성됩니다(그렇지 않기 때문입니다). 나는 게으른 사람이기 때문에(그리고 당신도 그럴 것이라고 확신합니다) 다음과 같은 별칭을 사용하고 싶습니다.

alias sudo='pass mydomain.tld/hostname/users/root/password | sudo --stdin'

pass에 익숙하지 않은 분들을 위해 설명하자면, 기본적으로 GnuPG를 사용하여 디렉토리 트리의 비밀번호를 암호화한 다음 pass directory/subdirectory/password비밀번호 복호화를 호출하고 이를 stdout에 인쇄합니다(물론 디렉토리는 선택 사항이며 루트 디렉토리에 저장된 모든 것을 섞을 수 있습니다. 그러나 이는 권장되지 않습니다.) 별칭의 목적은 하루에 수십 번 실행되는 것을 방지하는 것입니다.

$ pass --clip root_password #Copy root password to clipboard 
$ sudo command
[sudo] password for root: paste
#command output (if the password is right of course)

그러나 alias 명령을 테스트하면 어떤 일이 발생합니까?

$ pass root_password | sudo --stdin --shell
[sudo] password for root: %
$

여기서 %로고는 색상이 반전되어 있습니다. 내 터미널에는 검정색 배경에 흰색 텍스트, %흰색 배경에 검정색 텍스트가 있고 약간의 흰색 강조 표시가 있으며 비밀번호가 해독된 후에 인쇄됩니다. 이것을 인쇄 [sudo] password for root:하고 이것을 인쇄하는 사이 %GPG는 나에게 개인 키 비밀번호를 요청했습니다. 어떤 경우에는 반전된 퍼센트 기호를 발견했으며, 어디서 언제 보았는지 인쇄되고 있다고 생각합니다. EOF(여기서는 조심하세요. 전혀 잘 모르겠습니다. 방법은 게시물 끝을 참조하세요. 이것을 인쇄하려면 %). 이 이상한 플래그 이후에는 루트가 아니라 echo $?복귀한다는 점에 유의하십시오 0.

pass root_password그래서 캐릭터에 대한 더 많은 정보를 얻을 수 있는지 확인하기 위해 출력의 16진수 덤프를 가져왔습니다 . 결과는 다음과 같습니다.

$ pass root_password
:4\?g&8n:6_F[`9Touc8Ls+L'8)>6!3,nNmUzR&Ub~w7NTd'^Lb0]`0`."u>tP\>XAspMLTt!@}=F6CP)NsSMYY7*xm'A!7`!n'tmAaGWoBhS|u4{k$*v/o|'%)mbXMw
$ pass root_password | hexdump -C
00000000  3a 34 5c 3f 67 26 38 6e  3a 36 5f 46 5b 60 39 54  |:4\?g&8n:6_F[`9T|
00000010  6f 75 63 38 4c 73 2b 4c  27 38 29 3e 36 21 33 2c  |ouc8Ls+L'8)>6!3,|
00000020  6e 4e 6d 55 7a 52 26 55  62 7e 77 37 4e 54 64 27  |nNmUzR&Ub~w7NTd'|
00000030  5e 4c 62 30 5d 60 30 60  2e 22 75 3e 74 50 5c 3e  |^Lb0]`0`."u>tP\>|
00000040  58 41 73 70 4d 4c 54 74  21 40 7d 3d 46 36 43 50  |XAspMLTt!@}=F6CP|
00000050  29 4e 73 53 4d 59 59 37  2a 78 6d 27 41 21 37 60  |)NsSMYY7*xm'A!7`|
00000060  21 6e 27 74 6d 41 61 47  57 6f 42 68 53 7c 75 34  |!n'tmAaGWoBhS|u4|
00000070  7b 6b 24 2a 76 2f 6f 7c  27 25 29 6d 62 58 4d 77  |{k$*v/o|'%)mbXMw|
00000080  0a                                                |.|
00000081

0a예 . 기호는 없지만 NLsudo 에 필요한 줄 바꿈(참조 )은 여기에 있습니다.man ascii%man sudo

하지만 잠깐, 이제 재미가 시작됩니다. 새로 시작된 터미네이터 창이나 tty에서 명령을 두 번 실행하면 다음과 같은 일이 발생합니다.

$ pass root_password | sudo --stdin --shell
[sudo] password for root: %
$ pass root_password | sudo --stdin --shell
zsh: parse error near `)'
$

그 이후로 zsh가 손상된 것 같습니다. 첫 번째 명령의 출력을 실행하려는 것처럼 보이지만 sudo두 번째 명령만 실행합니다. 다음은 "깨진" 셸의 몇 가지 예입니다.

$ echo testpw | sudo --stdin --shell
zsh: command not found: testpw
$ echo testpw | cat
testpw
$ echo testpw | wc --bytes
11
$ echo anothertestpw | sudo --stdin --shell
zsh: command not found: anothertestpw
$ pass root_password | sudo --stdin --shell
zsh: parse error near `)'
$ exec zsh
$ echo testpw | sudo --stdin --shell
zsh: command not found:testpw

여기서 무슨 일이 일어나고 있는 걸까요? 표시된 것처럼 zsh 인스턴스를 다른 인스턴스( evec )로 교체해도 exec zsh문제가 해결되지 않습니다. 여기서 실제로 깨진 것은 무엇입니까? 비아, zsh 또는 sudo?

그럼 계속해서 좀 더 테스트해 보겠습니다. 새 터미널 창에서 또 다른 16진수 덤프를 실행해 보겠습니다(분명히 zsh를 "정상" 상태로 복원하는 방법을 모르기 때문입니다).

$ pass root_password | sudo --stdin --shell 2>&1 | hexdump -C
00000000  5b 73 75 64 6f 5d 20 70  61 73 73 77 6f 72 64 20  |[sudo] password |
00000010  66 6f 72 20 72 6f 6f 74  3a 20                    |for root: |
0000001a
$

퍼센트 기호는 어디로 갔나요? 전혀 모른다. 여기에는 없고 아직 루트도 아니지만 쉘은 이제 손상된 상태입니다(이 "손상된 상태"라는 이름을 적절하게 지정하는 방법을 전혀 모릅니다). 이후에 명령을 실행하면 pass root_password | sudo --stdin --shell이전과 동일한 오류 메시지가 표시됩니다. tty와 터미네이터, 심지어 사악한 %캐릭터에서도 모든 것이 동일하다는 점은 주목할 가치가 있습니다.

이것이 제가 도움이 필요할 수 있다고 생각할 수 있는 모든 정보입니다. 저는 Arch Linux를 사용하고 있으며 언급된 모든 패키지는 오늘(지구상의 위치에 따라 2017년 4월 16일 또는 15일, 현재 프랑스 오전 4시) 현재 최신 버전입니다. 즉( pacman -Q출력):

  • 커널: 4.10.9-1
  • zsh:5.3.1-2
  • 스도: 1.8.19.p2-1
  • 통과됨: 1.7.1-1
  • gnupg:2.1.20-1
  • 터미네이터: 1.91-5

여기 하나 있어요협회내 .zshrc의 경우에도 도움이 될 수 있습니다.

%약속한 대로, 이 악성 플래그를 얻는 몇 가지 단계는 다음과 같습니다 pass. 문자를 인쇄하기 위해 뒤에 새 줄을 추가하지 않고 여러 줄의 암호를 만들고 Ctrl+를 누르면 암호가 완성됩니다. 텍스트 줄 끝에 있는 +를 D클릭하는 것만으로는 충분하지 않으며 두 번 클릭해야 한다는 것을 알았습니다 . 그러나 해당 여러 줄 비밀번호에 후행 라인을 추가하는 경우 해당 비밀번호를 한 번 클릭하면 이 내용을 표시하지 않고 비밀번호 입력이 종료됩니다.CtrlD

$ pass insert --multiline test_evil_percent
Enter contents of test_evil_percent and press Ctrl+D when finished:
fooReturn
barCtrl+DCtrl+D%
거기는!

하지만 후행 줄이 있으면 나타나지 않으며 Ctrl+를 D한 번만 누르면 프롬프트가 표시됩니다.

$ pass insert --multiline test_evil_percent
Enter contents of test_evil_percent and press Ctrl+D when finished:
fooReturn
barReturn
Ctrl+D
$

따라서 근본적인 질문은 다음과 같습니다. 별칭을 어떻게 작동하게 만들 수 있나요? 궁금하신 분들을 위해 저는 사용자 정의 스크립트 디렉토리에 pass root_password환경 변수를 호출하고 SUDO_ASKPASS내 파일의 해당 스크립트 전달에 설정하는 스크립트 줄을 작성했습니다 . .zshrc또 다른 질문은 다음과 같습니다. 여기서 zsh와 sudo는 정확히 무엇입니까?


2017-04-16 편집: 솔루션

감사해요마이클 호머내 문제에 대해 다음 솔루션을 선택했습니다.

  • askpass-sudo다음을 호출하는 한 줄짜리 스크립트를 작성하세요.pass root_password
  • SUDO_ASKPASS내 환경 변수를 .zprofile스크립트 경로 로 설정합니다.
  • alias sudo='sudo -A'내 줄에 이 줄을 추가하세요 ..zshrc

바라보다! 이제 루트 쉘을 적절하게 얻을 수 sudo command있도록 루트 암호를 해독하는 데 필요한 GnuPG 키 암호를 요청합니다 .sudo --shell

답변1

pass root_password | sudo --stdin --shell효과가 있었습니다. pass비밀번호를 인쇄하고 sudo이를 읽고 쉘을 시작합니다. 최종 출력은 쉘 pass의 출력 및 입력 스트림이 passEOF에서 성공적으로 종료되었음을 의미합니다.

후속 실행sudo시간 초과 창인증이 필요하지 않으므로 쉘이 비밀번호 자체를 수신하고 이를 명령으로 실행하려고 시도하여 오류가 발생합니다.


쉘을 원하거나 stdin에서 읽는 일부 명령을 실행하는 경우 별칭이 작동하지 않을 수 있습니다(그러나 별칭이 필요하지 않은 명령의 경우에는 작동합니다). 원하는 경우 실행 별칭을 설정한 pass root_password | sudo --stdin true다음 제한 시간을 활용하여 암호 없이 명령을 실행할 수 있습니다.

원하는 동작을 제공하기 위해 일종의 AskPass 도우미를 구축할 수도 있습니다. pass다음을 통해 비밀번호를 제공 합니다 .묻다로 구성되어 있기 때문입니다 sudo.

관련 정보