Bash 스크립트 실행과 가져오기 사이의 이상한 불일치

Bash 스크립트 실행과 가져오기 사이의 이상한 불일치

나는 이것이 매우 설명적인 제목이 아니라는 것을 알고 있습니다(제안 환영). 그러나 사실 나는 이 문제로 몇 시간 동안 어려움을 겪었고 문제의 원인이 어디에 있는지 전혀 모릅니다.

로컬 네트워크의 피어 간 CLI 채팅을 위한 간단한 Bash 스크립트를 작성했습니다.

#!/usr/bin/env bash

# Usage: ./lanchat <local_ip>:<local_port> <remote_ip>:<remote_port>

# set -x
set -o errexit -o nounset -o pipefail

IFS=':' read -a socket <<< "$1"
LOCAL_IP=${socket[0]}
LOCAL_PORT=${socket[1]}

IFS=':' read -a socket <<< "$2"
REMOTE_IP=${socket[0]}
REMOTE_PORT=${socket[1]}

RECV_FIFO=".tmp.lanchat"

trap "rm '$RECV_FIFO'; kill 0" EXIT

mkfifo "$RECV_FIFO"

# EDIT: As per @Kamil Maciorowski's suggestion, removing the `-q 0` part below solves the issue.
while true; do nc -n -l -q 0 -s "$LOCAL_IP" -p "$LOCAL_PORT" > "$RECV_FIFO"; done &

TMUX_TOP="while true; do cat '$RECV_FIFO'; done"
TMUX_BOTTOM="while IFS= read -r line; do nc -n -q 0 '$REMOTE_IP' '$REMOTE_PORT' <<< \$line; done"

tmux new "$TMUX_TOP" \; split -v "$TMUX_BOTTOM"

IP 172.16.0.2의 머신은 Debian 11을 실행하는 VPS이고, 172.16.0.100은 Arch를 실행하는 로컬 컴퓨터입니다.

양쪽 프롬프트에서 수동으로 명령을 실행하면 원하는 결과를 얻을 수 있으며, 이를 통해 네트워크 통신에 문제가 없고 스크립트 논리가 올바른지 확인할 수 있습니다.

## VPS (Debian) side as follows; exchange IPs for local (Arch) side.
$ mkfifo .tmp.lanchat
$ while true; do nc -n -l -q 0 -s 172.16.0.2 -p 1234 > .tmp.lanchat; done &
$ tmux new "while true; do cat .tmp.lanchat; done" \; split -v "while IFS= read -r line; do nc -n -q 0 172.16.0.100 1234 <<< \$line; done"
## Test communication in both directions: all right; then CTRL-C twice to exit both tmux panels
$ kill %1; rm .tmp.lanchat

그러나 양쪽을 모두 스크립트로 실행하면 로컬 쪽(Arch)만 서버(Debian)의 메시지를 인쇄합니다. 서버가 내 로컬 컴퓨터에서 아무것도 인쇄하지 않습니다. Trace 를 사용하여 실행하면 set -x양쪽의 모든 것이 변수가 올바른 값으로 대체되어 수동으로 입력한 명령과 똑같아 보입니다.

이제 이상한 점은 Arch 측에서 스크립트를 실행하고 Debian 측에서 프롬프트(위에 표시된)를 따르면 모든 것이 다시 잘 작동한다는 것입니다. 또한 Arch 측에서 스크립트를 실행했지만원천데비안 쪽에서도 매우 잘 작동합니다.

두 가지 모두에 자세한 출력을 추가하십시오.CNCCalling Arch의 측면에도 각인이 있습니다 Connection to 172.16.0.2 1234 port [tcp/*] succeeded!. 그러나 tee log.txt통화에 추가CNCDebian 측의 청취 모드에서는 아무것도 캡처되지 않습니다:

#...
while true; do
    nc -n -l -q 0 -s "$LOCAL_IP" -p "$LOCAL_PORT" | tee log.txt > "$RECV_FIFO";
done &
#...

나는 가능한 모든 순서로 두 피어 사이의 연결을 설정하려고 노력합니다. 고아나 좀비 인스턴스가 없는지 확인하기 위해 서버와 로컬 컴퓨터를 다시 시작하기도 했습니다.CNC어떻게든 탐지를 회피하는 소켓을 수용하세요.

현재 Debian과 Arch는 서로 다른 버전의CNC. 따라서 표면적으로 이것은 가능한 설명처럼 들립니다. 하지만 데비안 측에서 스크립트가 잘 작동한다는 사실이 이러한 가능성을 배제합니까?

여기서 무슨 일이 일어나고 있는 걸까요?

답변1

Debian 12(localhost에서 localhost로, 별도의 작업 디렉터리)에서 스크립트를 테스트하고 문제를 확인했습니다. 내 nc것은 netcat-traditional 1.10-47(즉, 출신이 아닌netcat-openbsd).

문제는 -q 0청각 이다 nc. 에서 man 1 nc:

-q seconds
stdin에서 EOF 후 지정된 시간(초)을 기다린 후 종료합니다. 초가 음수이면 영원히 기다리십시오.

nc리스너는 종료하기 전에 들어오는 연결을 기다리는 것처럼 보이지만 -q 0들어오는 데이터를 기다리지는 않습니다. -q 0도구는 일반적으로 중간에 종료되므로 연결 설정과 데이터 전송은 별도의 이벤트입니다 . 이것은 내 시험 듣기 대회입니다.nc 때때로들어오는 데이터를 파이프로 중계합니다.

예기치 않은 동작을 유발하는 EOF는 작업 제어가 없는 셸이 비동기 명령을 실행할 때(terminate하기 위해 &, 이는 청취 실행 루프를 사용하는 방법 nc) 표준 입력을 /dev/null동등한 문서로 리디렉션해야 하기 때문에 즉시 발생합니다.

스크립트를 받으면 대화형 셸이 이를 해석합니다. 아마도 작업 제어가 활성화된 bash일 것입니다(대화형 bash의 기본 동작). 그렇다면 별도의 프로세스 그룹에서 백그라운드 루프를 실행하지만 표준 입력은 여전히 ​​터미널에 연결되어 있습니다(일반적으로 이를 통해 fg작업을 백그라운드로 수행하고 입력할 수 있습니다). 백그라운드 작업의 경우 SIGTTIN의 입력을 터미널에서 훔칠 수 없으며 EOF가 발생하지 않습니다. 이렇게 하면 스크립트를 가져왔을 때 가져오지 않고 스크립트를 실행할 때 발생하는 문제가 리스너 nc에서 발생하지 않습니다 .-q 0

-q 1듣기를 지정하면 nc실제로 도움이 될 것입니다(비록 이론적으로는 여전히 활성화되어 있지만). 사용하거나 -q -1(영원히 기다리기) 간단히 생략하는 것이 더 낫다고 생각합니다 -q(내 테스트의 기본 동작)~인 것 같다"영원히 기다리는" 상태가 됩니다.)

-q 0(tmux 내에서) 연결이 의미가 있기 때문에 페이로드를 보낸 후 즉시 종료 nc하고 싶을 것입니다 .nc

nc아치의 동작은 다르며, 아마도 다르기 때문일 수도 있고 당시 OS의 전반적인 스트레스가 게임에 영향을 미치기 때문일 수도 있습니다.

교훈은 다음과 같습니다. nc+ nc -l쌍이 한 방향으로만 데이터를 보내는 경우(행당 하나의 쌍 사용) -q 0발신자에게는 유용한 옵션이지만 수신자에게는 불필요하며 어떤 경우에는 해로울 수도 있습니다.


다음과 같이 개선해야 할 영역이 더 있습니다.

  • 코드 주입 취약점( ./lanchat <local_ip>:<local_port> <remote_ip>:<remote_port>"'; rogue command'")이 있습니다.
  • nc한쪽 끝이나 다른 쪽 끝이 듣지 않는 짧은 시간이 있습니다.
  • 한 쌍의 nc는 전체 "세션"을 처리하는 데 충분합니다.

여기서는 이러한 문제에 대해 논의하지 않겠지만 대체 스크립트에 대한 개요를 제공할 수 있습니다.

#!/usr/bin/env bash

target="$(tmux new -dP 'tail -f /dev/null')"
uptty="$(tmux display-message -p -F '#{pane_tty}' -t "$target")"
tmux split -t "$target" -v "
   rlwrap tee    >(sed -u 's/^/      < /' | ts %H:%M >${uptty@Q}) \
   | nc ${*@Q} > >(sed -u 's/^/> /'       | ts %H:%M >${uptty@Q})
"
tmux a -t "$target"

스크립트에는 bash(자체 및 내부적으로 tmux)가 필요합니다. nc예를 들어 제공하려는 인수를 사용하여 실행할 수 있습니다 .

  • 첫 번째는 청취자입니다 ./lanchat -n -l -s 192.168.11.22 -p 2345.
  • 그런 다음 연결 가장자리가 있습니다 ./lanchat 192.168.11.22 2345.

단일 nc연결은 nc양방향의 모든 통신을 처리합니다. 이 스크립트는 타임스탬프( ts필요한 경우 두 인스턴스를 모두 삭제할 수 있음)와 줄 편집을 위한 readline(필요한 경우 삭제할 수 있음)에 사용됩니다. 이식성이 없습니다. 제거하지 않는 한 버퍼링 문제가 발생하지 않습니다.| ts %H:%Mrlwraprlwrapsed -used-uts

bash 5.2.15, tmux 3.3a에서 테스트되었습니다.

관련 정보