Index: tests/phpunit/tests/attachment/adjecentImageLink.php
===================================================================
--- tests/phpunit/tests/attachment/adjecentImageLink.php	(revision 0)
+++ tests/phpunit/tests/attachment/adjecentImageLink.php	(working copy)
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @group attachment
+ * @ticket 24688
+ */
+
+class Tests_Attachment_AdjecentImageLink extends WP_UnitTestCase {
+	function test_previous_image_link() {
+		global $post;
+		$parent_id = self::factory()->post->create();
+		$args = array(
+			'file' => 'image.jpg',
+			'post_parent' => $parent_id,
+			'post_mime_type' => 'image/jpeg',
+		);
+		$prev_attachment_id = self::factory()->attachment->create_object( $args );
+		$curr_attachment_id = self::factory()->attachment->create_object( $args );
+
+		$post = get_post( $curr_attachment_id );
+		setup_postdata( $post );
+		ob_start();
+		previous_image_link();
+		$prev_link = ob_get_clean();
+
+		$this->assertEquals( wp_get_attachment_link( $prev_attachment_id, 'thumbnail', true, false, false ), $prev_link );
+	}
+
+	function test_next_image_link() {
+		global $post;
+		$parent_id = self::factory()->post->create();
+		$args = array(
+			'file' => 'image.jpg',
+			'post_parent' => $parent_id,
+			'post_mime_type' => 'image/jpeg',
+		);
+		$curr_attachment_id = self::factory()->attachment->create_object( $args );
+		$next_attachment_id = self::factory()->attachment->create_object( $args );
+
+		$post = get_post( $curr_attachment_id );
+		setup_postdata( $post );
+		ob_start();
+		next_image_link();
+		$next_link = ob_get_clean();
+
+		$this->assertEquals( wp_get_attachment_link( $next_attachment_id, 'thumbnail', true, false, false ), $next_link );
+	}
+}

Index: src/wp-includes/media.php
===================================================================
--- src/wp-includes/media.php	(revision 43665)
+++ src/wp-includes/media.php	(working copy)
@@ -2793,46 +2793,46 @@
  *
  * @since 2.5.0
  *
+ * @uses _previous_image_post_where_filter_once()
+ * @uses _next_image_post_where_filter_once()
+ *
  * @param bool         $prev Optional. Whether to display the next (false) or previous (true) link. Default true.
  * @param string|array $size Optional. Image size. Accepts any valid image size, or an array of width and height
  *                           values in pixels (in that order). Default 'thumbnail'.
  * @param bool         $text Optional. Link text. Default false.
  */
 function adjacent_image_link( $prev = true, $size = 'thumbnail', $text = false ) {
-	$post        = get_post();
-	$attachments = array_values(
+	$adjacent = $prev ? 'previous' : 'next';
+	$order    = $prev ? 'DESC' : 'ASC';
+	$post     = get_post();
+
+	add_filter( 'posts_where', "_{$adjacent}_image_posts_where_filter_once" );
+
+	$attachment = array_values(
 		get_children(
 			array(
-				'post_parent'    => $post->post_parent,
-				'post_status'    => 'inherit',
-				'post_type'      => 'attachment',
-				'post_mime_type' => 'image',
-				'order'          => 'ASC',
-				'orderby'        => 'menu_order ID',
+				'post_parent'      => $post->post_parent,
+				'post_status'      => 'inherit',
+				'post_type'        => 'attachment',
+				'post_mime_type'   => 'image',
+				'orderby'          => array(
+					'menu_order' => $order,
+					'ID'         => $order,
+				),
+				'posts_per_page'   => 1,
+				'suppress_filters' => false,
 			)
 		)
 	);
 
-	foreach ( $attachments as $k => $attachment ) {
-		if ( $attachment->ID == $post->ID ) {
-			break;
-		}
-	}
-
 	$output        = '';
 	$attachment_id = 0;
 
-	if ( $attachments ) {
-		$k = $prev ? $k - 1 : $k + 1;
-
-		if ( isset( $attachments[ $k ] ) ) {
-			$attachment_id = $attachments[ $k ]->ID;
-			$output        = wp_get_attachment_link( $attachment_id, $size, true, false, $text );
-		}
+	if ( ! empty( $attachment ) ) {
+		$attachment_id = $attachment[0]->ID;
+		$output        = wp_get_attachment_link( $attachment_id, $size, true, false, $text );
 	}
 
-	$adjacent = $prev ? 'previous' : 'next';
-
 	/**
 	 * Filters the adjacent image link.
 	 *
@@ -2850,6 +2850,28 @@
 }
 
 /**
+ * Append to WHERE clause for selecting only the previous image.
+ *
+ * @param string $where The WHERE clause of the query.
+ */
+function _previous_image_posts_where_filter_once( $where ) {
+	remove_filter( 'posts_where', __FUNCTION__ );
+	$post = get_post();
+	return "$where AND (menu_order < $post->menu_order OR (menu_order = $post->menu_order AND ID < $post->ID))";
+}
+
+/**
+ * Append to WHERE clause for selecting only the next image.
+ *
+ * @param string $where The WHERE clause of the query.
+ */
+function _next_image_posts_where_filter_once( $where ) {
+	remove_filter( 'posts_where', __FUNCTION__ );
+	$post = get_post();
+	return "$where AND (menu_order > $post->menu_order OR (menu_order = $post->menu_order AND ID > $post->ID))";
+}
+
+/**
  * Retrieves taxonomies attached to given the attachment.
  *
  * @since 2.5.0
