acsc asia

=====================Cowsay as a Service=======================

đề cung cấp docker nên mình sẽ dựng ở local và debug


import Koa from 'koa';
import Router from '@koa/router';
import auth from 'koa-basic-auth';
import bodyParser from 'koa-bodyparser';
import child_process from 'child_process';

const settings = {};

const style = `<style>
body { padding: 2rem; }
.form input[type=text] { padding: .5rem 1rem; font-size: 1rem; display: block; margin-bottom: 1rem; }
.form input[type=submit] { display: block; margin-bottom: 1rem; color: #fff; background-color: #000; padding: .5rem 1rem; font-size: 1rem; border: none; }
.color-setting { margin-bottom: 1rem; }
.cowsay { font-size: 2rem; background: #beead6; padding: 0.5rem 1rem; }
</style>`;

const app = new Koa();
const router = new Router();

// basic auth
if (process.env.CS_USERNAME && process.env.CS_PASSWORD) {
  app.use(auth({
    name: process.env.CS_USERNAME,
    pass: process.env.CS_PASSWORD
  }))
}

app.use(async (ctx, next) => {
  ctx.state.user = ctx.cookies.get('username');
  await next();
});

router.get('/', (ctx, next) => {
  ctx.body = `
${style}
<h1>Welcome to Cowsay as a Service</h1>
<p>Before start the service, please enter your name.</p>
<form action="/cowsay" method="GET" class="form">
  <input type="text" name="user" placeholder="Username">
  <input type="submit" value="Login">
</form>
<script>
document.querySelector('form').addEventListener('submit', () => {
  const username = document.querySelector('input[name="user"]').value;
  document.cookie = 'username=' + username;
});
</script>
`;
  next();
});

router.get('/cowsay', (ctx, next) => {
  const setting = settings[ctx.state.user];
  const color = setting?.color || '#000000';

  let cowsay = '';
  if (ctx.request.query.say) {
    const result = child_process.spawnSync('/usr/games/cowsay', [ctx.request.query.say], { timeout: 500 });
console.log(result);
          cowsay = result.stdout.toString();
  }

  ctx.body = `
${style}
<h1>Cowsay as a Service</h1>

<details class="color-setting">
  <summary>Color Preferences</summary>
  <form action="/setting/color" method="POST">
    <input type="color" name="value" value="${color}">
    <input type="submit" value="Change Color">
  </form>
</details>

<form action="/cowsay" method="GET" class="form">
  <input type="text" name="say" placeholder="hello">
  <input type="submit" value="Say">
</form>

<pre style="color: ${color}" class="cowsay">
${cowsay}
</pre>
`;
});

router.post('/setting/:name', (ctx, next) => {
  if (!settings[ctx.state.user]) {
    settings[ctx.state.user] = {};
  }
  const setting = settings[ctx.state.user];//
  setting[ctx.params.name] = ctx.request.body.value;=?setting.nmame=aaa
  ctx.redirect('/cowsay');
});

app.use(bodyParser());
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);

cơ bản như sau, chỉ có 1 file index.js là file xử lý chính và flag ở biến môi trường.

ở index thì có những route sau:

index

/cowsay

cowsay tạo một child process mới rồi gọi binary cowsay ra với tham số là query say mình truyền vào.

/setting/:name

cơ bản route này chỉ xung quanh dòng này

setting[ctx.params.name] = ctx.request.body.value;

nhìn biết ngay prototype-pollution

mình xin giải thích sơ về bug này như sau, cơ bản mọi thứ trong js đều là object, tham chiếu nguyên mẫu của một object sử dụng __proto__

ở vd trên thì mình đã ghi đè được biến cc của object array, vậy những biến được tạo kể từ khi bị ô nhiễm sẽ đều có thể gọi cc

ở vd này mình thậm chí ghi đè được biến , vậy mình có thể ghi đè bất kỳ biến nào ư?

áp dụng vào bài này như sau.

ở route setting, setting được gán bằng state.user thứ được lấy từ cookie. vậy sẽ ra sao mình gửi một request như dưới đây

câu trả lời là

như đã thấy, biến color đã bị override thành công, vậy tiếp theo mình sẽ ghi đè gì đây? nhìn lại code ở hàm bò say ta thấy, nó tạo một child process mới và gọi binary, nên mình sẽ ghi đè một biết nào đó khi thực hiện tạo child process mới.

ở đây có một biến gọi là option khi truyền vào nhằm định nghĩa các biến như env,shell,cwd ,...

thứ đập vào mình là env,và shell.

ở biến shell khi được set bằng true thì có thể thực thi đc các symboy như

$(ls)

ez rce,.

cách 2: overide biến môi trường và sử dụng require, mình tham khảo writeup dưới đây

đầu tiên ghi đè shell = node, nhưng ở đây là môi trường docker khác nhau nên phải sử dụng path của docker là /usr/local/bin/node

tiếp theo ghi đè env , gọi child process để trigger lệnh.

đến đây thì có thể thực hiện rev shell.

=======================Favorite Emojis=======================

bài này có source đơn giản như sau, chỉ cần access / sẽ có flag.

but no, nên mình sẽ tìm tiếp source và đọc.

theo file conf thì đây có vẻ là 1 trình kết xuất động

đây là mô tả ngắn gọn về nó như sau, khi bot request thì nginx sẽ proxy nó sang rendering để render.

đây là khi người dùng bình thường request

còn đây là với UA là bot

vì khi ta request tới / nginx sẽ chuyển ngay tới prerender , để xác định xem nó có phải bot hay không, nếu không thì render index.html , vì nhiệm vụ của ta là phải touch được api:8000 nên ta phải điều hướng sao cho prerender tới api:8000

nhận thấy nginx lấy $host của mình và pass vào trình kết xuất, nên thử thay hostheader bằng url của server mình.

lập tức có connect từ bot tới.

bài toán quay trở lại ssrf để touch được api:8000 nên mình sẽ sử dụng chuyển hướng mở.

host file html và lặp lại bước trên.

sau khi bot render thì ngay lập tức sẽ được redirect về / và chúng ta có flag.

==================Baby Developer==============================

ở bài này cung cấp cho mình source code, và thêm cmd để ssh vào server?????? lúc này mình khác thắc mắc tại sao.

nhưng kệ cứ nhìn vào source trước đã

nhìn qua cơ cấu file thì thấy bài này có 4 service, genflag để lấy flag, mobiviewer để view cái gì đó, website là ssh service,...

luồng xử lý chính ở mobiviewer nên mình sẽ đọc vào đó đầu tiên.

ở file main, chỉ chú trọng vào route này

có vẻ như đang tìm cách chống lại ssrf và đặc biệt redis .ở hàm này chỉ đơn giản kiếm tra xem url cung cấp có chứa black list không, và sau đó request.get url được cấp, và đẩy vào query redis

tiếp theo có một service khác nữa là worker nó là một header chrome less có nhiệm vụ lấy data từ redis và request tới, đồng thời screenshot với một hình

16x16 ở server chall và 640x1136 ở local test.

đầu tiên khi tiếp cận bài này mình sẽ nghĩ theo hướng, ssrf redis override ssh key, rồi sử dụng sshkey login và to genflag.

nhưng không, sau một vài lần thử mình đã đổi mẹ chall khác làm. sau khi giải xong bò say thì mình mới quay lại challange này, chall này dễ nhưng ít sovle hơn nhưng chall khác chắc tại vì ai cũng đang sa đà vào ssrf redis.

sau khi quay trở lại thì mình nhận được hint từ idol giới trẻ

đến đây thì mình nghĩ mọi cách để đọc id_rsa

đây là mình khi thử ở local, vậy service bot của nó bị abuse to read file.

nhưng vì ssh nằm trong blacklist nên mình thử để chuyển hướng mở ở server như bài trước.

kết quả...

vì trên server chall chỉ có screenshot với hình 16x16 nên mình phải tiếp tục tìm cách.. tiếp theo mình mạnh dạn thử luôn xss để đọc content và nhận ra service này không chỉ có read file mà thậm chi là cors để access all domain lol. sau khi fetch thành công thì việc tiếp theo của mình chỉ cần ssh vào với id_rsa là có flag

#ps link toàn bộ source

https://github.com/hinemo123/WriteUp/tree/master/ACSC

Last updated

Was this helpful?